首页 > 解决方案 > Pandas:根据一列的子字符串搜索和另一列的反向创建一个新列

问题描述

我想根据一列的子字符串搜索和另一列的倒数在 Pandas 数据框中创建一个新列。这是一些数据:

import pandas as pd
import numpy as np

df = pd.DataFrame({'Manufacturer':['ABC-001', 'ABC-002', 'ABC-003', 'ABC-004', 'DEF-123', 'DEF-124', 'DEF-125', 'ABC-987', 'ABC-986', 'ABC-985'],
                   'Color':['04-Red', 'vs Red - 07', 'Red', 'Red--321', np.nan, np.nan, np.nan, 'Blue', 'Black', 'Orange'],
                  })


    Manufacturer    Color
0   ABC-001         04-Red
1   ABC-002         vs Red - 07
2   ABC-003         Red
3   ABC-004         Red--321
4   DEF-123         NaN
5   DEF-124         NaN
6   DEF-125         NaN
7   ABC-987         Blue
8   ABC-986         Black
9   ABC-985         Orange

我希望能够创建一个Country基于以下逻辑命名的新列:

a) 如果Manufacturer列包含子字符串 'ABC' 并且Color列包含子字符串 'Red',则将 'United States' 写入Country

b) 如果该Manufacturer列包含子字符串“DEF”,则将“Canada”写入该Country

c) 如果该Manufacturer列包含子字符串“ABC”并且该Color列不包含子字符串“Red”,则将“England”写入该Country列。

我的尝试如下:

df['Country'] = np.where((df['Manufacturer'].str.contains('ABC')) & (df['Color'].str.contains('Red', na=False)), 'United States',  # the 'a' case
                np.where(df['Manufacturer'].str.contains('DEF', na=False), 'Canada',  # the 'b' case
                np.where((df['Manufacturer'].str.contains('ABC')) & (df[~df['Color'].str.contains('Red', na=False)]), 'England',  # the 'c' case
                         'ERROR')))

但是,这会出现以下错误:

TypeError: Cannot perform 'rand_' with a dtyped [float64] array and scalar of type [bool]

错误消息表明这可能是运算符优先级的问题,如下所述:

pandas 比较引发 TypeError:无法将 dtyped [float64] 数组与 [bool] 类型的标量进行比较

Python 错误:TypeError:无法将 dtyped [float64] 数组与 [bool] 类型的标量进行比较

我相信我在这里正确地使用了括号(尽管也许我不是)。

有谁看到这个错误的原因?(或者知道更优雅的想要完成这个?)

提前致谢!

标签: pythonpandasnumpy

解决方案


您不想在df此处建立索引,因此只需执行以下操作:

只是改变:(df[~df['Color'].str.contains('Red', na=False)])

至:~df['Color'].str.contains('Red', na=False)

它应该可以工作。

另外,如果您想将其分解以提高可读性并消除一些重复,我建议您这样做:

# define the parameters that define the Country variable in another table
df_countries = pd.DataFrame(
    {'letters': ['ABC', 'DEF', 'ABC'],
     'is_red': [True, False, False],
     'Country': ['United States', 'Canada', 'England']})

# add those identifying parameters to your current table as temporary columns
df['letters'] = df.Manufacturer.str.replace('-.*', '')
df['is_red'] = df.Color.str.contains('Red', na=False)

# merge the tables together and drop the temporary key columns
df = df.merge(df_countries, how='left', on=['letters', 'is_red'])
df = df.drop(columns=['letters', 'is_red'])

或者更简洁:

in_col = lambda col, string: df[col].str.contains(string, na=False)

conds = {'United States': in_col('Manufacturer', 'ABC') & in_col('Color', 'Red'),
         'Canada': in_col('Manufacturer', 'DEF'),
         'England': in_col('Manufacturer', 'ABC') & ~in_col('Color', 'Red')}

df['Country'] = np.select(condlist=conds.values(), choicelist=conds.keys())

推荐阅读