Pandas 两个数据帧的布尔索引

Pandas Boolean indexing with two dataframes

我有两个 pandas 数据帧:

df1
'A' 'B'
 0   0
 0   2
 1   1
 1   1
 1   3

df2
'ID' 'value'
 0   62
 1   70
 2   76
 3   4674
 4   3746

我想将 df.value 作为新列 D 分配给 df1,但只是在 df.A == 0 时。 df1.Bdf2.ID 应该是标识符。

示例输出:

df1
'A' 'B' 'D'
 0   0   62
 0   2   76
 1   1   NaN
 1   1   NaN
 1   3   NaN

我尝试了以下方法:

df1['D'][ df1.A == 0 ] = df2['value'][df2.ID == df1.B]

但是,由于 df2 和 df1 的长度不同,我得到了一个 ValueError。

ValueError: Series lengths must match to compare

这肯定是由于最后一部分的布尔索引:[df2.ID == df1.B]

有谁知道如何在不需要遍历数据帧的情况下解决问题?

非常感谢!

==============

编辑回复@EdChum:它与示例数据完美配合,但我的真实数据有问题。 df1 是一个巨大的数据集。 df2 看起来像这样:

df2
    ID  value
0   1   1.00000
1   2   1.00000
2   3   1.00000
3   4   1.00000
4   5   1.00000
5   6   1.00000
6   7   1.00000
7   8   1.00000
8   9   0.98148
9   10  0.23330
10  11  0.56918
11  12  0.53251
12  13  0.58107
13  14  0.92405
14  15  0.00025
15  16  0.14863
16  17  0.53629
17  18  0.67130
18  19  0.53249
19  20  0.75853
20  21  0.58647
21  22  0.00156
22  23  0.00000
23  24  0.00152
24  25  1.00000

合并后,输出如下:首先是 133 乘以 0.98148,然后是 47 乘以 0.00025,然后继续使用来自 df2 的更多值序列,直到最后出现一个 NaN 条目序列...

Out[91]: df1
    A   B   D
0   1   3   0.98148
1   0   9   0.98148
2   0   9   0.98148
3   0   7   0.98148
5   1   21  0.98148
7   1   12  0.98148
...     ...     ...     ...
2592    0   2   NaN
2593    1   17  NaN
2594    1   16  NaN
2596    0   17  NaN
2597    0   6   NaN

知道这里可能发生了什么吗?都是int64.

==============

这里有两个包含重现问题的数据的 csv。

df1: https://owncloud.tu-berlin.de/public.php?service=files&t=2a7d244f55a5772f16aab364e78d3546

df2: https://owncloud.tu-berlin.de/public.php?service=files&t=6fa8e0c2de465cb4f8a3f8890c325eac

重现:

import pandas as pd

df1 = pd.read_csv("../../df1.csv")
df2 = pd.read_csv("../../df2.csv")

df1['D'] = df1[df1.A == 0].merge(df2,left_on='B', right_on='ID', how='left')['value']

这个有点棘手,这里有 2 个步骤,首先是 select 只有 df 中 'A' 为 0 的行,然后合并到另一个 df 'B' 和 'ID' 匹配,但执行 'left' 合并,然后 select 来自此的 'value' 列并分配给 df:

In [142]:

df['D'] = df[df.A == 0].merge(df1, left_on='B',right_on='ID', how='left')['value']
df
Out[142]:
   A  B   D
0  0  0  62
1  0  2  76
2  1  1 NaN
3  1  1 NaN
4  1  3 NaN

将其分解将显示正在发生的事情:

In [143]:
# boolean mask on condition
df[df.A == 0]
Out[143]:
   A  B   D
0  0  0  62
1  0  2  76
In [144]:
# merge using 'B' and 'ID' columns
df[df.A == 0].merge(df1, left_on='B',right_on='ID', how='left')
Out[144]:
   A  B   D  ID  value
0  0  0  62   0     62
1  0  2  76   2     76

完成上述所有操作后就可以直接赋值了:

df['D'] = df[df.A == 0].merge(df1, left_on='B',right_on='ID', how='left')['value']

这是有效的,因为它会与左侧的 idnex 对齐,因此任何缺失的值都会自动分配 NaN

编辑

另一种似乎适用于您的真实数据的方法是使用 map 为您执行查找,map 接受字典或系列作为参数并将查找相应的值,在这种情况下,您需要将索引设置为 'ID' 列,这会将您的 df 减少到只有 'Value' 列的一个:

df['D'] = df[df.A==0]['B'].map(df1.set_index('ID')['value'])

所以上面像以前一样执行布尔索引,然后在 'B' 列上调用 map 并在 [=] 上设置索引后在另一个 df 中查找相应的 'Value' 48=].

更新

我查看了你的数据和我的第一个方法,我明白了为什么会失败,左侧 df 的对齐失败,所以你在连续的行中得到 1192 个值,然后其余的行是 NaN 到第 2500 行。

如果您像这样在左侧应用相同的蒙版,则有效:

df1.loc[df1.A==0, 'D'] = df1[df1.A == 0].merge(df2,left_on='B', right_on='ID', how='left')['value']

所以这正确地屏蔽了左侧的行并分配了合并的结果