【问题标题】:Merge and update dataframes based on a subset of their columns根据列的子集合并和更新数据框
【发布时间】:2018-02-24 16:54:14
【问题描述】:

我想知道是否有最快的代码来替换两个for循环,假设df大小非常大。在我的真实案例中,每个数据框是 200 行和 25 列。

data_df1 = np.array([['Name','Unit','Attribute','Date'],['a','A',1,2014],['b','B',2,2015],['c','C',3,2016],\
                 ['d','D',4,2017],['e','E',5,2018]])
data_df2 = np.array([['Name','Unit','Date'],['a','F',2019],['b','G',2020],['e','H',2021],\
                 ['f','I',2022]])
df1 = pd.DataFrame(data=data_df1)
print('df1:')
print(df1)
df2 = pd.DataFrame(data=data_df2)
print('df2:')
print(df2)
row_df1 = [1,2,5]
col_df1 = [1,3]
row_df2 = [1,2,3]
col_df2 = [1,2]
for i in range(0,len(row_df1)):
    for j in range(0, len(col_df1)):
        df1.set_value(row_df1[i],col_df1[j], df2.loc[row_df2[i],col_df2[j]])
print('df1 after operation:')
print(df1)

预期输出:

df1:
      0     1          2     3
0  Name  Unit  Attribute  Date
1     a     A          1  2014
2     b     B          2  2015
3     c     C          3  2016
4     d     D          4  2017
5     e     E          5  2018
df2:
      0     1     2
0  Name  Unit  Date
1     a     F  2019
2     b     G  2020
3     e     H  2021
4     f     I  2022
df1 after operation:
      0     1          2     3
0  Name  Unit  Attribute  Date
1     a     F          1  2019
2     b     G          2  2020
3     c     C          3  2016
4     d     D          4  2017
5     e     H          5  2021

我试过了:

df1.loc[[1,2,5],[1,3]] = df2.loc[[1,2,3],[1,2]]
print('df1:')
print(df1)
print('df2:')
print(df2)

但结果如下。有意想不到的楠。

df1:
      0     1          2     3
0  Name  Unit  Attribute  Date
1     a     F          1   NaN
2     b     G          2   NaN
3     c     C          3  2016
4     d     D          4  2017
5     e   NaN          5   NaN
df2:
      0     1     2
0  Name  Unit  Date
1     a     F  2019
2     b     G  2020
3     e     H  2021
4     f     I  2022

提前感谢任何提供帮助的人。

【问题讨论】:

    标签: python pandas dataframe


    【解决方案1】:

    一些清洁:

    def clean_df(df):
        df.columns = df.iloc[0]
        df.columns.name = None        
        df = df.iloc[1:].reset_index()
    
        return df
    
    df1 = clean_df(df1)
    df1
       index Name Unit Attribute  Date
    0      1    a    A         1  2014
    1      2    b    B         2  2015
    2      3    c    C         3  2016
    3      4    d    D         4  2017
    4      5    e    E         5  2018
    
    df2 = clean_df(df2)
    df2    
       index Name Unit  Date
    0      1    a    F  2019
    1      2    b    G  2020
    2      3    e    H  2021
    3      4    f    I  2022
    

    使用merge,指定on=Name,因此不考虑其他列。

    cols = ['Name', 'Unit_y', 'Attribute', 'Date_y']
    df1 = df1.merge(df2, how='left', on='Name')[cols]\
                  .rename(columns=lambda x: x.split('_')[0]).fillna(df1)
    
    df1
      Name Unit Attribute  Date
    0    a    F         1  2019
    1    b    G         2  2020
    2    c    C         3  2016
    3    d    D         4  2017
    4    e    H         5  2021
    

    【讨论】:

    • @John 如果你坚持说你得到了错误的答案,那是因为你的数据,而不是我的问题。我应该指出,这是您第二次这样做,并且拒绝承认为回答您的问题所付出的努力以及随之而来的受损数据。
    • @COLDSPEED 非常感谢您的帮助。这只是一个事实,我在笔记本中看到的结果,使用 df1.T.reset_index().T 后没有第一行列索引 0、1、2、3,即“名称”、“单位”, .etc 是作为 df1.columns.values 返回的结果。
    • @John 那就跳过这一步吧。
    • @COLDSPEED 我已将您的回答标记为回答我的问题
    • @John 很高兴为您提供帮助。另一个呢?
    【解决方案2】:

    另一种基于转置数据帧和填充的合并和删除重复项的方法,即

    new_df = df1.merge(df2,on=[0],how='outer').T.set_index(0).sort_index()
            .ffill().reset_index().drop_duplicates(0,keep='last').T.dropna()
    
    0 2 3 5 0 属性 日期 名称 单位 1 1 2019 年 2 2 2020 b G 3 3 2016 c C 4 4 2017 年 5 5 2021 e H

    解释

    df1.merge(df2,on=[0],how='outer').T.set_index(0).sort_index()
    

    转置后的数据框将给出数据框,以便我们可以应用 ffill 来填充 nan 值

    1 2 3 4 5 6 0 属性 1 2 3 4 5 NaN 日期 2014 2015 2016 2017 2018 NaN 日期 2019 2020 NaN NaN 2021 2022 名称 a b c d e f 单位 A B C D E NaN 单位 F G NaN NaN H I
    .ffill().reset_index().drop_duplicates(0,keep='last')
    

    这将使用前行数据和 reset_index 填充 nan 值,删除子集 0 的重复项,并保留最后一个将保留完全填充的行。

    0 1 2 3 4 5 6 0 属性 1 2 3 4 5 NaN 2 日期 2019 2020 2016 2017 2021 2022 3 命名 a b c d e f 5 单位 F G C D H I
    .T.dropna()
    

    这将旋转数据框删除具有 nan 值的行,从而产生所需的输出。

    【讨论】:

      【解决方案3】:

      我还发现下面的代码可以满足我的要求,并且比两个 for 循环快得多。

      df1.loc[[1,2,5],[1,3]] = df2.loc[[1,2,3],[1,2]].values
      

      【讨论】:

        猜你喜欢
        • 2015-08-11
        • 2023-01-08
        • 1970-01-01
        • 1970-01-01
        • 2023-04-07
        • 2019-06-20
        • 2017-07-31
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多