Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/354.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 根据两列的比较从第二个数据帧添加列_Python_Pandas_Dataframe_Join_Merge - Fatal编程技术网

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 float
a=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]])