基于两个其他数据框的索引合并两个数据框

6

我是新手,尝试阅读文档并尝试各种示例,但我正在处理的问题确实让我困惑。

我有以下两个数据帧(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值下,一个项的trait A和trait B的值。

global_index可以大致看作是一个“时间”单位。

通过以下两个映射器数据帧(DataA/DataB),将每个数据帧与global_index之间的映射关系进行处理:

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

简单来说,对于给定的全局索引(例如:1),映射器将定义与该全局索引相关联的各个数据帧(DataA或DataB)中的行列表。
例如,对于全局索引值为0: - 在DF DataA中,行0到2与全局索引0相关联。 - 在DF DataB中,行0到1与全局索引0相关联。
另一个例子,对于全局索引值为2: - 在DF DataB中,行2到4与全局索引2相关联。 - 在DF DataA中,没有与全局索引2相关联的行。
表示的范围[start_row,start_row + num_rows)不交叉,并且在其各自的数据帧(DataA、DataB)中表示唯一的序列/行范围。
简而言之,DataA或DataB中的任何一行都不会在多个范围中找到。
我想合并这些数据帧,以便获得以下数据帧。
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

在最终的数据框中任何一对全局索引/item_id,都会出现以下情况之一:
  1. 同时存在valueA和valueB的值
  2. 仅存在valueA的值
  3. 仅存在valueB的值
其中要求是,如果给定全局索引/item的值只有一个(例如只有valueA没有valueB),则使用缺失值的最后一个值。

你能分享一些数据示例吗?最好是以易于使用的格式呈现。 - AMC
@AMC - 当然可以,不过上面的例子没有传达出结构来吗?我遇到的问题是如何将映射器框架与数据框A/B相关联,因为它们是数据框A/B与全局索引唯一的关系。 - Lucinda Rigetti
在合并的DF中,当global_index == 2时,即使DataA_mapper表中没有列出,你是如何得到valueA值的,@Lucinda Rigetti,你能解释一下吗? - Sido4odus
1
我并没有真正理解最后一个要求。所以对于全局索引2,在DataA中没有关联的行,但当它们合并时,您显示该全局索引的valueA为A4、A2和A5。这是什么逻辑? - K753
@K753 我同意。对于此问题中给定的数据,上面的预期输出是否正确? - Scott Boston
@K753和Sidou Mahmoud,我添加了更多的澄清,希望这样可以解决任何未解决的问题。 - Lucinda Rigetti
2个回答

1

首先,您可以使用函数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

优秀的解决方案,完美地运作!感谢您纠正问题中的数据。 - Lucinda Rigetti
一个小问题 - 如果我们向DataA_mapper(或其他mapper)添加一行,其中重复全局索引但给出另一个start/num_rows范围,例如:(3,8,2),并在DataA中添加适当的行。_____当我运行此代码时,我收到以下错误:“分类类别必须是唯一的”,这是在调用cut时发生的。什么是正确的解决方法,以确保item_id的valueA,valueB始终设置为由global_index/startidx/num_row组合定义的最后一组值?__如果您有解决方案,我很乐意创建另一个问题... - Lucinda Rigetti

0

我还没有测试过这个,因为我没有好的测试数据,但我认为像这样的东西应该可以工作。基本上,它不是尝试执行某种复杂的连接,而是构建一系列列表来保存您的数据,然后在最后将它们组合成一个最终的数据框。

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 的结果转换为元组?这相当不符合惯例... - AMC
(1) 我的命名规范确实有欠缺,这是一个公正的观点。(2) 因为我只是一周前开始学习pandas,并不知道我可以这样做,感谢您的建议。(3) 有没有更推荐的方法将多个列表转换为pandas数据框?list(zip())是我从官方pandas网站直接链接到的教程中学到的方法: https://nbviewer.jupyter.org/urls/bitbucket.org/hrojas/learn-pandas/raw/master/lessons/01%20-%20Lesson.ipynb - krock
(3) 压缩是可以的,只是我认为 list() 是不必要的。 - AMC

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