如何遍历数据帧的所有行,以将查找功能应用于字符串值,并将结果应用于新列?

时间:2019-07-11 20:51:44

标签: python pandas function

我有一个数据框,其中每一行(人)都有几列个人数据。我想应用一个函数在区域列表中查找每个人的城市或州,然后将结果应用到同一数据框中的新列“ Region”。

我已经能够使用非常简化的数据框(包括颜色和车辆类别)进行相同的操作(请参见下文)。但是,当我尝试使用个人数据进行操作时,它的工作方式将不同,而且我也不知道为什么。

我已经阅读了很多关于lambda函数的主题,但我认为我要问的内容太复杂了。大多数解决方案都处理数字数据,而我使用的是字符串,但是正如我所说,我能够使其与一个数据集一起使用。显然我是新来的。我也很感谢有关如何将新列作为函数的一部分进行构建的建议,而不必将其作为单独的步骤进行构建,但这并没有像主要问题一样使我感到沮丧。

此示例有效:

# Python: import pandas
import pandas as pd
# Simple dataframe. Empty column 'type'.
df = pd.DataFrame({'one':['1','2','3','4','5','6','7','8'], 
                  'two':['A','B','C','D','E','F','G','H'], 
                  'three': ['car','bus','red','blue','truck','pencil','yellow','green'],
                  'type':''})

df显示:

    one two three   type
0   1   A   car     
1   2   B   bus     
2   3   C   red     
3   4   D   blue    
4   5   E   truck   
5   6   F   pencil  
6   7   G   yellow  
7   8   H   green   

现在定义列表和自定义功能:

# Definte lists of colors and vehicles
colors = ['red','blue','green','yellow']
vehicles = ['car','truck','bus','motorcycle']

# Create function 'celltype' to return values based on x
def celltype (x):
    if x in colors: return 'color' 
    elif x in vehicles: return 'vehicle'
    else: return 'other'

然后构造一个循环遍历每一行并应用函数:

# Write loop to iterate through df rows and apply function 'celltype' to column 'three' in each row
for index, row in df.iterrows(): 
    row['type'] = celltype(row['three'])

在这种情况下,结果就是我想要的:

    one two three   type
0   1   A   car     vehicle
1   2   B   bus     vehicle
2   3   C   red     color
3   4   D   blue    color
4   5   E   truck   vehicle
5   6   F   pencil  other
6   7   G   yellow  color
7   8   H   green   color


此示例不起作用,我也不知道为什么:

df1 = pd.DataFrame({'Last Name':['SMITH','JONES','WILSON','DOYLE','ANDERSON'], 'First Name':['TOM','DICK','HARRY','MICHAEL','KEVIN'],
                    'Code':[12,34,56,78,90], 'Deparment':['Research','Management','Maintenance','Marketing','IT'],
                    'City':['NEW YORK','BOSTON','SAN FRANCISCO','DALLAS','DETROIT'], 'State':['NY','MA','CA','TX','MI'], 'Region':''}) 

df1显示:

    Last Name   First Name  Code    Deparment   City        State   Region
0   SMITH       TOM         12  Research        NEW YORK    NY  
1   JONES       DICK        34  Management      BOSTON      MA  
2   WILSON      HARRY       56  Maintenance     SAN FRANCISCO   CA  
3   DOYLE       MICHAEL     78  Marketing       DALLAS      TX  
4   ANDERSON    KEVIN       90  IT              DETROIT     MI  

再次定义列表和功能:

# Define lists for regions
east = ['NEW YORK','BOSTON']
west = ['SAN FRANCISCO','LOS ANGELES']
south = ['TX']

# Create function 'region' to return values based on x
def region (x):
    if x in east: return 'east' 
    elif x in west: return 'west'
    elif x in south: return 'south'
    else: return 'other'

# Write loop to iterate through df1 rows and apply function 'region' to column 'City' in each row
for index, row in df1.iterrows(): 
    row['Region'] = region(row['City'])
    if row['Region'] == 'other': row['Region'] = region(row['State'])

这将导致df1不变。 “地区”列仍为空白。我们应该看到“东方”,“东方”,“西方”,“南方”,“其他”。代码中的唯一区别是附加的'if'语句,用于按状态捕获达拉斯(这是我的真实世界数据集所需要的)。但是我认为那条线是声音,没有它我会得到相同的结果。

2 个答案:

答案 0 :(得分:1)

首先,applyiterrows速度很慢,所以请永远不要使用它们。

在这种情况下,我通常要做的是创建一对forwardbackward字典:

forward = {'east': east,
           'west': west,
           'south': south}

backward = {x:k for k,v in forward.items() for x in v}

然后使用map更新。由于您希望基于两列进行更新,因此fillna会有所帮助:

df1['Region'] = (df1['State'].map(backward)
                    .fillna(df1['City'].map(backward))
                    .fillna('other')
                )

给予:

  Last Name First Name  Code    Deparment           City State Region
0     SMITH        TOM    12     Research       NEW YORK    NY   east
1     JONES       DICK    34   Management         BOSTON    MA   east
2    WILSON      HARRY    56  Maintenance  SAN FRANCISCO    CA   west
3     DOYLE    MICHAEL    78    Marketing         DALLAS    TX  south
4  ANDERSON      KEVIN    90           IT        DETROIT    MI  other

答案 1 :(得分:0)

您的问题是使用iterrows。通常,您应该永远不要修改要迭代的内容。在这种情况下,iterrows正在创建数据的副本,因此实际上并没有修改您的df1。根据情况的不同,该副本可能会发生,也可能不会发生,因此通常希望避免执行此类操作。

您可以通过直接使用at调用数据框来确保它修改了原始文件:

for index, row in df1.iterrows(): 
    df1.at[index, 'Region'] = region(row['City'])
    if df1.at[index, 'Region'] == 'other': df1.at[index, 'Region'] = region(row['State'])