在pandas中复制一些行并更改一些值

5

I have a pandas DataFrame looking like this:

From    To    Val
GE      VD    1000
GE      VS    1600
VS      VD    1500
VS      GE     600
VD      GE    1200
VD      VS    1300

我希望替换每一行中“from”或“to”列没有“GE”的行,将其替换为两行,一行中“from”列为“GE”,另一行中“to”列为“GE”。 例如,在上面的示例中,我将用以下两行替换第三行:
GE VD 1500
VS GE 1500
我尝试使用“apply”,但无法返回正确的数据框。例如:
def myfun(row):
    if "GE" not in (row["from"], row["to"]):
        row1=pd.DataFrame(row).T
        row2=row1.copy()
        row1["from"]="GE"
        row2["to"]="GE"
        return pd.concat([row1, row2])
    else:
        return pd.DataFrame(row).T

给出了奇怪的结果:
>> df.apply(myfun, axis=1)
   Val  from  to
0  Val  from  to
1  Val  from  to
2  Val  from  to
3  Val  from  to
4  Val  from  to
5  Val  from  to

尽管我的函数看起来正确:
>> myfun(df.loc[5])
  Val from  to
5  13   GE  VD
5  13   VS  GE

我可以想到一种方法,通过将我的数据框分为两个子数据框来进行过滤,一个包含需要复制的行,另一个包含其他行。然后复制第一个数据框,进行更改,并将所有三个数据框合并在一起。但这样做很丑陋。有没有人能提出更优雅的方式?
换句话说,应用的函数是否可以返回一个数据框,就像我们在 R 中使用 ddply 那样?
谢谢。
2个回答

5

过滤:

In [153]: sub = df[(~df[['From', 'To']].isin(['GE'])).all(1)]

In [154]: sub
Out[154]: 
  From  To   Val
2   VS  VD  1500
5   VD  VS  1300

[2 rows x 3 columns]


In [179]: good = df.ix[df.index - sub.index]

In [180]: good
Out[180]: 
  From  To   Val
0   GE  VD  1000
1   GE  VS  1600
3   VS  GE   600
4   VD  GE  1200

[4 rows x 3 columns]

定义一个函数,将所需的值作为DataFrame返回:

def new_df(row):
    return pd.DataFrame({"From": ["GE", row["From"]],
                         "To": [row["To"], "GE"],
                         "Val": [row["Val"], row["Val"]]})

将该函数应用于行:

In [181]: new = pd.concat([new_df(y) for _, y in x.iterrows()], axis=0, ignore_index=True)

In [182]: new
Out[182]: 
  From  To   Val
0   GE  VD  1500
1   VS  GE  1500
2   GE  VS  1300
3   VD  GE  1300

[4 rows x 3 columns]

并将它们拼接在一起
In [183]: pd.concat([good, new], axis=0, ignore_index=True)
Out[183]: 
  From  To   Val
0   GE  VD  1000
1   GE  VS  1600
2   VS  GE   600
3   VD  GE  1200
4   GE  VD  1500
5   VS  GE  1500
6   GE  VS  1300
7   VD  GE  1300

[8 rows x 3 columns]

当我尝试第一行时,为什么会出现以下错误的任何建议:AttributeError: 'DataFrame'对象没有'isin'属性? - julieth
谢谢,虽然下面的解决方案稍微短一些,但它确实有效。 - user3190381
越短越好,而且看起来我的代码只有大约6行 :)@julieth,你知道你正在使用的pandas版本吗?它在即将发布的“0.13”中得到了增强。您可以对每个列应用函数并连接结果来进行一些混乱的解决方法。如果需要帮助,请告诉我! - TomAugspurger

1
这个程序分两步完成。如果添加一个else条件,可以缩短代码长度,将保留不变的行连接起来。然而,我认为这样更易读,并且由于我们使用itertuples遍历行,因此成本是线性的,并且仅在需要时形成每个元组(而不是同时形成所有行的一个大列表)。
同样地,在if语句中弹出一行并将两个新行连接到原始数据对象df的相应位置,以避免创建keeper_rows的内存成本。除非DataFrame很大,否则通常不值得为此类任务进行这种优化。
keeper_rows = df.ix[[i for i,x in enumerate(df.itertuples()) if 'GE' in x[0:2]]]
for row_as_tuple in df.itertuples():
    from_other, to_other, val = row_as_tuple
    if "GE" not in (from_other, to_other):
        new_rows = {"From":["GE", from_other], 
                    "To"  :[to_other, "GE"], 
                    "Val" :[val, val]}
        keeper_rows = pandas.concat([keeper_rows, pandas.DataFrame(new_rows)], 
                                    ignore_index=True)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接