【问题标题】:Duplicating some rows and changing some values in pandas在熊猫中复制一些行并更改一些值
【发布时间】:2014-01-13 14:33:04
【问题描述】:

我有一个看起来像这样的 pandas DataFrame:

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”,另一行在“from”列中包含“GE” “到”列。 在上面的示例中,我将用以下两行替换第三行:
GE VD 1500
VS 通用电气 1500

我尝试使用“应用”,但不知道如何返回正确的数据框。例如

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

我可以想出一种方法,将我的数据帧过滤到两个子数据帧中,一个包含需要重复的行,另一个包含需要重复的行。然后复制第一个数据帧,进行更改并将所有三个 DF 整理在一起。但它很丑。谁能提出更优雅的方式?

换句话说,应用函数是否可以返回一个 DataFrame,就像在 R 中我们使用 ddply 所做的那样?

谢谢

【问题讨论】:

    标签: python pandas apply


    【解决方案1】:

    过滤:

    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' object has no attribute 'isin'?
    • 谢谢,虽然下面的解决方案略短,但它可以工作
    • 越短越好,而且看起来我的大约有 6 行 :) @julieth,你知道你使用的是什么版本的熊猫吗?它在即将发布的0.13 中得到了增强。您可以通过将函数应用于每一列并连接结果来做一些混乱的解决方法。如果您需要帮助,请告诉我!
    【解决方案2】:

    这使用两次通过。如果您添加一个else 条件来连接将保持不变的行,则它可能会被缩短。但是,我发现这更具可读性,并且由于我们使用itertuples 遍历行,因此这里的成本是线性的,我们只是根据需要形成每个元组(不是同时为所有行提供一个大的元组列表) .

    同样,您可以在if 语句中弹出一行并将其位置的两个新行连接回原始数据对象df,这样您就不会产生创建@987654325 的内存成本@。除非 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)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多