【问题标题】:Pandas: creating a new column conditional on substring searches of one column and inverse of another columnPandas:根据一列的子字符串搜索和另一列的反向创建一个新列
【发布时间】:2021-03-01 04:56:58
【问题描述】:

我想根据一列的子字符串搜索和另一列的倒数在 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”并且ColorNOT 包含子字符串“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 comparison raises TypeError: cannot compare a dtyped [float64] array with a scalar of type [bool]

Python error: TypeError: cannot compare a dtyped [float64] array with a scalar of type [bool]

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

有人看到这个错误的原因吗? (或者知道更优雅的想要实现这个?)

提前致谢!

【问题讨论】:

    标签: python pandas numpy


    【解决方案1】:

    这是一种简单直接的方法:

    country = []
    
    for index, row in df.iterrows():
        
        if 'DEF' in row['Manufacturer']:
            country.append('Canada')
        elif 'ABC' in row['Manufacturer']:
            if 'Red' in row['Color']:
                country.append('United States')
            else:
                country.append('England')
        else:
            country.append('')
            
    df['Country'] = country
    

    当然会有更有效的方法来解决这个问题,而无需循环遍历整个数据帧,但在几乎所有情况下,这应该就足够了。

    【讨论】:

    • 从 OP 的矢量化 np.where 解决方案切换到超慢 iterrows 解决方案将是一个倒退
    【解决方案2】:

    你不想在这里索引到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())
    

    【讨论】:

      【解决方案3】:

      另一种方法是使用np.select(list of conditions, list of choices)

      conditions=[(df['Manufacturer'].str.contains('ABC')) & (df['Color'].str.contains('Red')),df['Manufacturer'].str.contains('DEF', na=False),(df['Manufacturer'].str.contains('ABC')) & (~df['Color'].str.contains('Red', na=False))]
      choices=['United States','Canada','England']
      df['Country']=np.select(conditions,choices)
      
      
      
        Manufacturer        Color        Country
      0      ABC-001       04-Red  United States
      1      ABC-002  vs Red - 07  United States
      2      ABC-003          Red  United States
      3      ABC-004     Red--321  United States
      4      DEF-123          NaN         Canada
      5      DEF-124          NaN         Canada
      6      DEF-125          NaN         Canada
      7      ABC-987         Blue        England
      8      ABC-986        Black        England
      9      ABC-985       Orange        England
      

      【讨论】:

        猜你喜欢
        • 2022-07-06
        • 1970-01-01
        • 1970-01-01
        • 2021-11-30
        • 2022-10-15
        • 2020-04-16
        • 1970-01-01
        • 1970-01-01
        • 2022-07-29
        相关资源
        最近更新 更多