【问题标题】:Merging two DataFrames based on indexes from two other DataFrames根据来自其他两个 DataFrame 的索引合并两个 DataFrame
【发布时间】:2020-05-03 17:32:34
【问题描述】:

我是 pandas 的新手,我尝试过阅读文档并尝试各种示例,但我正在解决的这个问题真的让我很难过。

我有以下两个数据框 (DataA/DataB),我想按 global_index/item/values 合并它们。

DataA                      DataB
row  item_id  valueA       row    item_id  valueB
0    x        A1           0      x        B1
1    y        A2           1      y        B2
2    z        A3           2      x        B3
3    x        A4           3      y        B4
4    z        A5           4      z        B5
5    x        A6           5      x        B6
6    y        A7           6      y        B7
7    z        A8           7      z        B8

项目列表(item_ids) 是有限的,两个数据帧中的每一个都代表给定 global_index 值的项目的特征值(特征 A,特征 B)。

global_index 大致可以认为是一个“时间”单位

每个数据帧(DataA/DataB)和 global_index 之间的映射是通过以下两个映射器 DF 完成的:

DataA_mapper
global_index  start_row  num_rows
0             0          3
1             3          2
3             5          3


DataB_mapper
global_index  start_row  num_rows
0             0          2
2             2          3
4             5          3

简单地说,对于给定的 global_index(例如:1),映射器会将行列表定义到与该 global_index 关联的各个 DF(DataA 或 DataB)中。

例如,对于 0 的 global_index 值:

  • 在 DF DataA 中,行 0..2 与 global_index 0 相关联
  • 在 DF DataB 中,行 0..1 与 global_index 0 相关联

另一个例子,global_index 值为 2:

  • 在 DF DataB 中,行 2..4 与 global_index 2 相关联
  • 在 DF DataA 中没有与 global_index 2 关联的行

显示的范围 [start_row,start_row + num_rows) 不相互重叠,并代表各自数据帧(DataA,DataB)中唯一的行序列/范围

简而言之,DataA 或 DataB 中的任何行都不会在多个范围内找到。


我想合并 DF,以便获得以下数据框:

row   global_index  item_id   valueA   valueB
0     0             x         A1        B1
1     0             y         A2        B2
2     0             z         A3        NaN
3     1             x         A4        B1
4     1             z         A5        NaN
5     2             x         A4        B3
6     2             y         A2        B4
7     2             z         A5        NaN
8     3             x         A6        B3
9     3             y         A7        B4
10    3             z         A8        B5
11    4             x         A6        B6
12    4             y         A7        B7
13    4             z         A8        B8

在最终的数据帧中,任何一对 global_index/item_id 都会有:

  1. valueA 和 valueB 的值
  2. 仅用于 valueA 的值
  3. 仅用于 valueB 的值

要求如果给定的 global_index/item 只有一个值(例如:valueA 但没有 valueB),则要使用的缺失值的最后一个值。

【问题讨论】:

  • 您能否以易于使用的格式分享一些示例数据?
  • @AMC - 当然我可以做到,尽管上面的示例没有传达结构 - 我遇到的问题是将映射器框架与数据帧 A/B 相关联 - 因为它们是只有 dataA/B 帧与 global_index 的关系。
  • 在合并的 DF 中,我不明白 global_index == 2 时你是如何获得 valueA 值的,即使它没有列在 DataA_mapper 表中,你能解释一下 @Lucinda里杰蒂?
  • 我不是很了解最后一个要求。因此,对于 global_index 2,DataA 中没有关联的行,但是当它们合并时,您将 valueA 显示为该全局索引的 A4、A2 和 A5。这样做的逻辑是什么?
  • @K753 我同意。对于这个问题中给出的数据,上面的预期输出是否正确?

标签: python pandas dataframe join merge


【解决方案1】:

我没有对此进行测试,因为我没有任何好的测试数据,但我认为这样的事情应该可行。基本上,这是在做的是,而不是尝试进行某种复杂的连接,它正在构建一系列列表来保存您的数据,然后您可以在最后将它们重新组合成最终的数据框。

DataA.set_index('row')
DataB.set_index('row')

#we're going to create the new dataframe from scratch, creating a list for each column we want
global_index = []
AValues = []
AIndex = []
BValues = []
BIndex = []

for indexNum in totalIndexes:
    #for each global index, we get the total number of rows to extract from DataA and DataB
    AStart = DataA_mapper.loc[DataA_mapper['global_index']==indexNum, 'start_row'].values[0]
    ARows = DataA_mapper.loc[DataA_mapper['global_index']==indexNum, 'num_rows'].values[0]
    AStop = AStart + Arows

    BStart = DataB_mapper.loc[DataB_mapper['global_index']==indexNum, 'start_row'].values[0]
    BRows = DataB_mapper.loc[DataB_mapper['global_index']==indexNum, 'num_rows'].values[0]
    BStop = BStart + Brows


    #Next we extract values from DataA and DataB, turn them into lists, and add them to our data
    AValues = AValues + list(DataA.iloc[AStart:AStop, 1].values)
    AIndex = AIndex + list(DataA.iloc[AStart:AStop, 0].values)
    BValues = BValues + list(DataB.iloc[BStart:BStop, 1].values)
    BIndex = BIndex + list(DataA.iloc[AStart:AStop, 0].values)

    #Create a temporary list of the current global_index, and add it to our data
    global_index_temp = []
    for row in range(max(ARows,Brows)):
        global_index_temp.append(indexNum)
    global_index = global_index + global_index_temp


#combine all these individual lists into a dataframe
finalData = list(zip(global_index, AIndex, BIndex, AValues, BValues))
df = pd.DataFrame(data = finalData, columns = ['global_index', 'item1', 'item2', 'valueA', 'valueB'])

#lastly you just need to merge item1 and item2 to get your item_id column

我已经尝试很好地评论它,希望总体计划是有意义的,您可以跟随并纠正我的错误或以自己的方式重写它。

【讨论】:

  • 如果你要写这么多代码,为什么不使用正确的命名约定呢?另外,为什么是list(.values) 而不是.tolist()?为什么要将 zip 的结果转换为元组?这是非常不习惯的......
  • (1) 由于我缺乏命名约定,这绝对是一个公平的观点 (2) 因为我一周前刚开始学习 pandas 并且不知道我可以,谢谢你的建议 (3)将多个列表转换为熊猫数据框的更推荐的方法? list(zip()) 是我在本教程中学到的方法,直接从 Pandas 官方网站链接到:nbviewer.jupyter.org/urls/bitbucket.org/hrojas/learn-pandas/raw/…
  • (3) Zip 没问题,只是我觉得list() 是不必要的。
【解决方案2】:

首先,您可以使用函数pd.cut 创建'global_index' 列:

for df, m in [(df_A, map_A), (df_B, map_B)]:

    bins = np.insert(m['num_rows'].cumsum().values, 0, 0) # create bins and add zero at the beginning
    df['global_index'] = pd.cut(df['row'], bins=bins, labels=m['global_index'], right=False)

接下来,您可以使用外连接到merge 两个数据框:

df = df_A.merge(df_B, on=['global_index', 'item_id'], how='outer')

最后你可以使用函数groupbyffill 来填充缺失值:

for val in ['valueA', 'valueB']:
    df[val] = df.groupby('item_id')[val].ffill()

输出:

   item_id  global_index  valueA  valueB
0        x             0      A1      B1
1        y             0      A2      B2
2        z             0      A3     NaN
3        x             1      A4      B1
4        z             1      A5     NaN
5        x             3      A6      B1
6        y             3      A7      B2
7        z             3      A8     NaN
8        x             2      A6      B3
9        y             2      A7      B4
10       z             2      A8      B5
11       x             4      A6      B6
12       y             4      A7      B7
13       z             4      A8      B8

【讨论】:

  • 优秀的解决方案,完美运行!并感谢您更正问题中的数据。
  • 一个小问题 - 如果我们要向 DataA_mapper(或其他映射器)添加一行,我们复制 global_index 但给出另一个 start/num_rows 范围,例如:(3, 8, 2)我们在 DataA 中添加适当的行。 _____ 当我运行它时,我收到以下错误:调用 cut 时出现“分类类别必须是唯一的”。解决这个问题的正确方法是什么,以确保 item_id 的 valueA、valueB 始终设置为 global_index/startidx/num_row 组合定义的最后一组值? __ 如果您有解决方案,我很乐意提出另一个问题...
猜你喜欢
  • 1970-01-01
  • 2022-01-12
  • 2016-10-24
  • 1970-01-01
  • 2015-11-23
  • 1970-01-01
  • 2017-09-02
  • 2022-11-10
相关资源
最近更新 更多