Python 根据两列的比较从第二个数据帧添加列
简单地说,我试图通过比较Python 根据两列的比较从第二个数据帧添加列,python,pandas,dataframe,join,merge,Python,Pandas,Dataframe,Join,Merge,简单地说,我试图通过比较air_id和hpg_id列的值,将df1中的latitude和longitude列添加到一个称为df2的较小数据帧中: 将纬度和经度添加到df2的技巧取决于如何与df1进行比较,这可能是以下三种情况之一: 当df2.air_id和df1.air_hd之间存在匹配时 当df2.hpg_id和df1.hpg_hd之间存在匹配时 当两者之间存在匹配时:[df2.air\u id,df2.hpg\u id]和[df1.air\u hd,df1.hpg\u id] 考虑到这
air_id
和hpg_id
列的值,将df1
中的latitude
和longitude
列添加到一个称为df2
的较小数据帧中:
将纬度
和经度
添加到df2
的技巧取决于如何与df1
进行比较,这可能是以下三种情况之一:
- 当
和df2.air_id
之间存在匹配时李>df1.air_hd
- 当
和df2.hpg_id
之间存在匹配时李>df1.hpg_hd
- 当两者之间存在匹配时:
和[df2.air\u id,df2.hpg\u id]
李>[df1.air\u hd,df1.hpg\u id]
df1
中的ignore\u me
列是如何从结果数据帧中漏掉的
以下是设置数据帧的代码:
data = { 'air_id' : [ 'air1', '', 'air3', 'air4', 'air2', 'air1' ],
'hpg_id' : [ 'hpg1', 'hpg2', '', 'hpg4', '', '' ],
'latitude' : [ 101.1, 102.2, 103, 104, 102, 101.1, ],
'longitude' : [ 51, 52, 53, 54, 52, 51, ],
'ignore_me' : [ 91, 92, 93, 94, 95, 96 ] }
df1 = pd.DataFrame(data)
display(df1)
data2 = { 'air_id' : [ '', 'air2', 'air3', 'air1' ],
'hpg_id' : [ 'hpg1', 'hpg2', '', '' ] }
df2 = pd.DataFrame(data2)
display(df2)
不幸的是,我无法将merge()
用于此任务。我当前的结果是一个数据帧,其中df1
中的所有列大部分都用NAN填充:
如何使用上述规则从
df1
复制这些特定列?这里有一种方法可以完成您要做的事情
首先使用merge()
两次。首先打开air\u id
,然后打开hpg\u id
。对于这两种情况,忽略键为空字符串时的简单情况
result = df2\
.merge(
df1[df1['air_id']!=''].drop(['hpg_id'], axis=1), on=['air_id'], how='left'
)\
.merge(
df1[df1['hpg_id']!=''].drop(['air_id'], axis=1), on=['hpg_id'], how='left'
)
print(result)
# air_id hpg_id ignore_me_x latitude_x longitude_x ignore_me_y \
#0 hpg1 NaN NaN NaN 91
#1 air2 hpg2 92.0 102.0 52.0 92
#2 hpg3 NaN NaN NaN 93
#
# latitude_y longitude_y
#0 101 51
#1 102 52
#2 103 53
但是,这会为所需的列创建重复项。(每次调用合并时,我都会删除另一个join键,以避免这些调用的列名重复。)
我们可以通过调整上描述的方法之一来合并这些值
更新 在合并产生重复条目的情况下,可以使用
这是一种没有合并的手动方式。它效率不高,但如果它对您的用例有足够的性能,那么它可能是可管理的
df1['lat_long'] = list(zip(df1['latitude'], df1['longitude']))
air = df1[df1['air_id'] != ''].set_index('air_id')['lat_long']
hpg = df1[df1['hpg_id'] != ''].set_index('hpg_id')['lat_long']
def mapper(row):
myair, myhpg = row['air_id'], row['hpg_id']
if (myair != '') and (myair in air):
return air.get(myair)
elif (myhpg != '') and (myhpg in hpg):
return hpg.get(myhpg)
elif (myair != '') and (myair in hpg):
return hpg.get(myair)
elif (myhpg != '') and (myhpg in air):
return air.get(myhpg)
else:
return (None, None)
df2['lat_long'] = df2.apply(mapper, axis=1)
df2[['latitude', 'longitude']] = df2['lat_long'].apply(pd.Series)
df2 = df2.drop('lat_long', 1)
# air_id hpg_id latitude longitude
# 0 hpg1 101 51
# 1 air2 hpg2 102 52
# 2 hpg3 103 53
使用集合和Numpy广播处理内容匹配。。。洒着仙尘
ids = ['air_id', 'hpg_id']
cols = ['latitude', 'longitude']
def true(s): return s.astype(bool)
s2 = df2.stack().loc[true].groupby(level=0).apply(set)
s1 = df1[ids].stack().loc[true].groupby(level=0).apply(set)
i, j = np.where((s1.values & s2.values[:, None]).astype(bool))
a = np.zeros((len(df2), 2), int)
a[i, :] = df1[cols].values[j]
df2.join(pd.DataFrame(a, df2.index, cols))
air_id hpg_id latitude longitude
0 hpg1 101 51
1 air2 hpg2 102 52
2 hpg3 103 53
详细信息
s2
看起来像这样
0 {hpg1}
1 {air2, hpg2}
2 {hpg3}
dtype: object
和s1
0 {air1, hpg1}
1 {hpg2}
2 {hpg3}
3 {air4, hpg4}
4 {air2}
dtype: object
关键是,我们希望找到该行中的任何内容是否与另一个数据帧中的行中的任何其他内容匹配。现在我可以使用广播和&
s1.values & s2.values[:, None]
array([[{'hpg1'}, set(), set(), set(), set()],
[set(), {'hpg2'}, set(), set(), {'air2'}],
[set(), set(), {'hpg3'}, set(), set()]], dtype=object)
但在布尔上下文中,空集的计算结果为False
,因此
(s1.values & s2.values[:, None]).astype(bool)
array([[ True, False, False, False, False],
[False, True, False, False, True],
[False, False, True, False, False]], dtype=bool)
现在我可以使用np.where
来显示这些True
s在哪里
i, j = np.where((s1.values & s2.values[:, None]).astype(bool))
print(i, j)
[0 1 1 2] [0 1 4 2]
这些是分别来自df2
和df1
的行。但我不需要两行1
,所以我创建了一个大小合适的空数组,期望覆盖行1
。我用df1
a = np.zeros((len(df2), 2), int)
a[i, :] = df1[cols].values[j]
a
array([[101, 51],
[102, 52],
[103, 53]])
然后我将其包装在一个
pd.DataFrame
中,并按照上面所示进行连接。是否hpg3
应该在air\u id
列中?那是打字错误吗?我觉得你的结果没有意义,除非它在hpg\u id
列中,或者我遗漏了什么?这是一个打字错误!感谢您指出这一点。对于舍入,请在创建a
时使用dtype floata=np.zeros((len(df2),2))
我最终选择了另一个答案作为正式的问题解决方案,因为它具有更快的性能和健壮性。您的解决方案花了约3分钟处理了2343828行的df1
,以及10行的df2
。尽管如此,我希望人们能够欣赏你对这种方法的伟大解释!我觉得它很有教育意义,也很有帮助。谢谢大家!@karlphillip你在用蟒蛇3吗?如果是这样,您必须使用列表
(我更新了答案)将对映射
的调用包装起来。下面是关于repl.it:和的工作示例。太好了!就性能而言,它与其他答案相比如何?如果你能用一个不起作用的示例更新你的问题,我可以尝试找到一种方法来修改它。@karlphillip这个示例的一个简单修复方法就是在最后添加result=result.drop_duplicates()
。让我知道,如果这解决了你更大的问题,我会更新我的答案。奇妙的解决方案!在极端情况下,如果有多个条目具有相同的air\u id
或hpg\u id
,并且两个实例都具有不同的纬度/经度坐标(出于某些奇怪的原因),则将该id的所有实例(具有不同的纬度/经度坐标)添加到结果表中,这绝对是一件好事!就性能而言,只需16秒即可处理2343828行的df1
,以及10行的df2
。太不可思议了,恭喜你!
i, j = np.where((s1.values & s2.values[:, None]).astype(bool))
print(i, j)
[0 1 1 2] [0 1 4 2]
a = np.zeros((len(df2), 2), int)
a[i, :] = df1[cols].values[j]
a
array([[101, 51],
[102, 52],
[103, 53]])