在Pandas的Lambda函数中使用Apply和多个if语句

17

我正在尝试根据类似于这样的数据框中人物的大小来推断一个分类:

      Size
1     80000
2     8000000
3     8000000000
...

我希望它看起来像这样:

      Size        Classification
1     80000       <1m
2     8000000     1-10m
3     8000000000  >1bi
...

我理解最理想的过程是应用像这样的lambda函数:

df['Classification']=df['Size'].apply(lambda x: "<1m" if x<1000000 else "1-10m" if 1000000<x<10000000 else ...)

我查看了几篇有关在lambda函数中使用多个if语句的帖子,这是一个示例链接,但是在使用多个if语句时,该语法对我来说无效,但在单个if条件中有效。

因此,我尝试了这个“非常优雅”的解决方案:

df['Classification']=df['Size'].apply(lambda x: "<1m" if x<1000000 else pass)
df['Classification']=df['Size'].apply(lambda x: "1-10m" if 1000000 < x < 10000000 else pass)
df['Classification']=df['Size'].apply(lambda x: "10-50m" if 10000000 < x < 50000000 else pass)
df['Classification']=df['Size'].apply(lambda x: "50-100m" if 50000000 < x < 100000000 else pass)
df['Classification']=df['Size'].apply(lambda x: "100-500m" if 100000000 < x < 500000000 else pass)
df['Classification']=df['Size'].apply(lambda x: "500m-1bi" if 500000000 < x < 1000000000 else pass)
df['Classification']=df['Size'].apply(lambda x: ">1bi" if 1000000000 < x else pass)

看来“通过”这个词似乎也不适用于lambda函数:

df['Classification']=df['Size'].apply(lambda x: "<1m" if x<1000000 else pass)
SyntaxError: invalid syntax

有没有关于在Pandas的apply方法中,一个lambda函数内部使用多个if语句的正确语法建议?无论是多行还是单行解决方案都可以。


任何建议关于在 Pandas 的 apply 方法中,使用 lambda 函数包含多个 if 语句的正确语法。无论是多行还是单行的解决方案对我来说都OK。

4
你可以直接使用一个函数。 - Anton vBR
AntonvBR,那会是什么样子? - aabujamra
编写一个函数来完成工作,并将名称作为参数传递给apply。 - JAB
你看过pd.cut或者categories吗? - cfort
4个回答

21

这里有一个可以基于它构建的简单示例:

基本上,lambda x: x.. 是一个函数的短小精悍的一行代码。真正需要的是您可以轻松重新创建的函数。

import pandas as pd

# Recreate the dataframe
data = dict(Size=[80000,8000000,800000000])
df = pd.DataFrame(data)

# Create a function that returns desired values
# You only need to check upper bound as the next elif-statement will catch the value
def func(x):
    if x < 1e6:
        return "<1m"
    elif x < 1e7:
        return "1-10m"
    elif x < 5e7:
        return "10-50m"
    else:
        return 'N/A'
    # Add elif statements....

df['Classification'] = df['Size'].apply(func)

print(df)

返回:

        Size Classification
0      80000            <1m
1    8000000          1-10m
2  800000000            N/A

1
我尝试了列出的方法,发现创建自己的函数是更灵活、透明的方式,可以避免一些意外后果。 - seakyourpeak
谢谢,是的,那可能是这种情况。然而,对于纯性能,应该使用像maxU示例这样的东西! - Anton vBR

7
您可以使用 pd.cut 函数:
bins = [0, 1000000, 10000000, 50000000, ...]
labels = ['<1m','1-10m','10-50m', ...]

df['Classification'] = pd.cut(df['Size'], bins=bins, labels=labels)

6

这里实际上是使用了应用Lambda函数,我只是想知道问题出在哪里...因为您的语法看起来没问题,而且它也能够正常工作...

df1= [80000, 8000000, 8000000000, 800000000000]
df=pd.DataFrame(df1)
df.columns=['size']
df['Classification']=df['size'].apply(lambda x: '<1m' if x<1000000  else '1-10m' if 1000000<x<10000000 else '1bi')
df

输出:

表格


2
使用Numpy的searchsorted函数
labels = np.array(['<1m', '1-10m', '10-50m', '>50m'])
bins = np.array([1E6, 1E7, 5E7])

# Using assign is my preference as it produces a copy of df with new column
df.assign(Classification=labels[bins.searchsorted(df['Size'].values)])

如果您想在现有的数据框中添加新列:
df['Classification'] = labels[bins.searchsorted(df['Size'].values)]

一些解释

来自文档:np.searchsorted

查找元素应插入以维护顺序的索引。

查找排序数组a中的索引,使得如果在这些索引之前插入v中相应的元素,则a的顺序将被保留。

labels数组的长度比bins大1。因为当某个值大于bins的最大值时,searchsorted返回-1。当我们切片labels时,这会获取最后一个标签。


当然很好 +1,但是真的需要在这里使用 df.assign 吗?我的观点是它不太易读。 - Anton vBR
1
@AntonvBR 我喜欢assign有很多原因。首先,因为当 OP 尝试我的代码时,他们不会自动破坏他们的数据框。其次,我更喜欢生成新数据框并将其重新分配给名称的设计模式。话虽如此,我将展示两种替代方案 (-: - piRSquared

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接