【问题标题】:How to merge efficiently two dataframes with a tolerance in python如何在python中有效地合并两个具有容差的数据框
【发布时间】:2018-07-25 05:05:16
【问题描述】:

目标:

我想使用 python 以一种有效的方式将两个数据帧 df1 和 df2 与容差合并。 df1 的形状为 (l, 2),df2 的形状为 (p, 13),其中 l

我想将 df1 的 Col0 与 df2 的 Col2 以容差“容差”合并。

例子:

df1:

Index, Col0, Col1
0, 1008.5155, n01

df2:

Index, Col0, Col1, Col2, Col3, Col4, Col5, Col6, ...
0, 0, 0, 510.0103, k03, 0, k05, k06, ... 
1, 0, 0, 1007.6176, k13, 0, k15, k16, ...
2, 0, 0, 1008.6248, k123, 0, k25, k26, ...

df3:

Index, Col0, Col1, Col2, Col3, Col4, Col5, Col6, ...
0, 1008.5155, 0.8979, 1007.6176, k03, n01, k05, k06, ...
1, 1008.5155, 0.1093, 1008.6248, k13, n01, k15, k16, ...

为了形象化,df3 的 col1 给出了 df1 和 df2 各自值的差异。因此,它必须小于公差。

我当前的解决方案需要很多时间并且需要大量内存。

 # Create empty list to collect matches
df3_list = []
df3_array = np.asarray(df3_list)

# loops to find matches. Fills array with matches
df3_row = np.asarray([0.0, 0.0, 0.0, 0.0, 0.0, 0, 0, 0, 0, 0, 0, 0, 0])

for n in range(len(df1)):
    for k in range(len(df2)):
        if abs(df1.iloc[n,0]-df2.iloc[k,2]) < tolerance:
            df3_row[0] = df1.iloc[n,0]
            df3_row[1] = abs(df1.iloc[n,0]-df2.iloc[k,2])
            df3_row[2] = df2.iloc[k,2]
            df3_row[3] = df2.iloc[k,3]
            df3_row[4] = df1.iloc[n,1]
            df3_row[5] = df2.iloc[k,5]
                       .
                       .
                       .

            df3_array = np.append(df3_array, df3_row)

# convert list into dataframe
df3 = pd.DataFrame(df3_array.T.reshape(-1,13), columns = header)

我也尝试过同时获得两个索引

[[n, k]  for n, k in zip(range(len(df1)), range(len(df2))) if abs(df1.iloc[n,0]-df2.iloc[k,2]) < tolerance]

但是,它只给了我一个空数组,所以我做错了。

对于各自的数组,我也尝试过使用

np.nonzero(np.isclose(df2_array[:, 2], df1_array[:,:,None], atol=tolerance))[-1]

但是,np.isclose + np.nonzero 只为我提供了 df2 的索引,而且比我的循环密集型方法要多得多。如果没有 df1 的相应索引,我有点迷茫。 我认为这最后一种方法是最有前途的,但我似乎无法合并数据集,因为这些值不是完全匹配的,而且最接近的匹配并不总是正确的解决方案。任何想法如何克服这个问题?

【问题讨论】:

    标签: python pandas numpy dataframe merge


    【解决方案1】:

    你需要把这个问题分成几部分

    1. 找到对应的收盘指数
    2. 在这些索引上加入 DataFrame
    3. 做额外的计算

    查找索引

    使用np.isclose,这是一个非常简单的生成器函数,它产生一个DataFrame,其中包含df1df2 的索引,它们对于df1 的每一行都很接近

    def find_close(df1, df1_col, df2, df2_col, tolerance=1):
        for index, value in df1[df1_col].items():
            indices = df2.index[np.isclose(df2[df2_col].values, value, atol=tolerance)]
            s = pd.DataFrame(data={'idx1': index, 'idx2': indices.values})
            yield s
    

    然后我们可以轻松地将它们连接起来,以使用包含不同索引的辅助 DataFrame。

    df_idx = pd.concat(find_close(df1, 'Col0', df2, 'Col2'), ignore_index=True)
    

    为了测试这一点,我向 df1 添加了第二条记录

    df1_str = '''Index, Col0, Col1
    0, 1008.5155, n01
    1, 510, n03'''
    
      idx1    idx2
    0 0   1
    1 0   2
    2 1   0
    

    加入数据帧

    使用pd.merge

    df1_close = pd.merge(df_idx, df1, left_on='idx1', right_index=True).reindex(columns=df1.columns)
    df2_close = pd.merge(df_idx, df2, left_on='idx2', right_index=True).reindex(columns=df2.columns)
    df_merged = pd.merge(df1_close, df2_close, left_index=True, right_index=True)
    
      Col0_x  Col1_x  Col0_y  Col1_y  Col2    Col3    Col4    Col5    Col6    ...
    0 1008.5155   n01 0   0   1007.6176   k13 0   k15 k16 ...
    1 1008.5155   n01 0   0   1008.6248   k123    0   k25 k26 ...
    2 510.0   n03 0   0   510.0103    k03 0   k05 k06 ...
    

    做额外的计算

    您需要重命名一些列,并分配它们之间的差异,但这应该是微不足道的

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-11-18
      • 2014-08-17
      • 2016-10-19
      • 2018-07-16
      • 2022-09-29
      • 1970-01-01
      • 2020-07-07
      • 2010-11-03
      相关资源
      最近更新 更多