根据来自另一个DataFrame的匹配ID,在pandas DataFrame列中处理值

时间:2017-07-07 14:26:09

标签: python-3.x pandas

我有两个数据框,如下面的例子:

import pandas as pd
import numpy as np

df = pd.DataFrame({'a': ['20', '50', '100'], 'b': [1, np.nan, 1],
                 'c': [np.nan, 1, 1]})
df_id = pd.DataFrame({'b': ['50', '4954', '93920', '20'],
                      'c': ['123', '100', '6', np.nan]})

print(df)
     a    b    c
0   20  1.0  NaN
1   50  NaN  1.0
2  100  1.0  1.0

print(df_id)
       b    c
0     50  123
1   4954  100
2  93920    6
3     20  NaN

对于df['a']中的每个标识符,如果df['b']中的任何行中没有匹配的标识符,我想要使df_id['b']中的值为空。我想对列df['c']做同样的事情。

我想要的结果如下:

result = pd.DataFrame({'a': ['20', '50', '100'], 'b': [1, np.nan, np.nan],
                 'c': [np.nan, np.nan, 1]})
print(result)
     a    b    c
0   20  1.0  NaN
1   50  NaN  NaN    # df_id['c'] did not contain '50'
2  100  NaN  1.0    # df_id['b'] did not contain '100'

我尝试这样做的地方是:

for i, letter in enumerate(['b','c']):
    df[letter] = (df.apply(lambda x: x[letter] if x['a']
                   .isin(df_id[letter].tolist()) else np.nan, axis = 1))

我得到的错误:

AttributeError: ("'str' object has no attribute 'isin'", 'occurred at index 0')

这是在Python 3.5.2,Pandas版本20.1

3 个答案:

答案 0 :(得分:0)

您可以使用此解决问题:

for letter in ['b','c']: # took off enumerate cuz i didn't need it here, maybe you do for the rest of your code
    df[letter] = df.apply(lambda row: row[letter] if row['a'] in (df_id[letter].tolist()) else np.nan,axis=1)

只需将isin替换为in

问题是,当您在df上使用apply时,x代表df rows,因此当您选择x['a']时,您实际上选择了一个元素。

但是,isin适用于引发错误的系列或类似列表的结构,因此我们只需使用in来检查该元素是否在列表中。

希望这很有帮助。如果您有任何疑问,请询问。

答案 1 :(得分:0)

Pandas New Column Calculation Based on Existing Columns Values调整难以找到的答案:

for i, letter in enumerate(['b','c']):
    mask = df['a'].isin(df_id[letter])
    name = letter + '_new'
    # for some reason, df[letter] = df.loc[mask, letter] does not work
    df.loc[mask, name] = df.loc[mask, letter]
    df[letter] = df[name]
    del df[name]

这不是很好,但似乎有效。

答案 2 :(得分:0)

如果您拥有更大的Dataframe并且性能对您很重要,您可以先构建一个掩码df,然后将其应用于您的数据帧。 首先创建掩码:

mask = df_id.apply(lambda x: df['a'].isin(x))
       b      c
0   True  False
1   True  False
2  False   True

这可以应用于原始数据框:

df.iloc[:,1:] = df.iloc[:,1:].mask(~mask, np.nan)
     a    b    c
0   20  1.0  NaN
1   50  NaN  NaN
2  100  NaN  1.0