我想将我的自定义函数(它使用if-else梯形图)应用于 Dataframe 每行的这六列(ERI_Hispanic
、ERI_AmerInd_AKNatv
、ERI_Asian
、ERI_Black_Afr.Amer
、ERI_HI_PacIsl
、ERI_White
)。
我尝试了其他问题的不同方法,但似乎仍然找不到我的问题的正确答案。关键是,如果一个人被算作西班牙裔,他们就不能被算作其他任何人。即使他们在另一个种族列中有一个“1”,他们仍然被算作西班牙裔,而不是两个或两个以上的种族。同样,如果所有ERI列的总和大于1,则将其计为两个或多个人种,不能计为唯一种族(西班牙裔除外)。
这几乎就像在每行中执行for循环,如果每条记录都满足某个条件,则将它们添加到一个列表中,并从原始列表中删除。
从下面的数据框中,我需要根据SQL中的以下规范计算一个新列:
标准
IF [ERI_Hispanic] = 1 THEN RETURN “Hispanic”
ELSE IF SUM([ERI_AmerInd_AKNatv] + [ERI_Asian] + [ERI_Black_Afr.Amer] + [ERI_HI_PacIsl] + [ERI_White]) > 1 THEN RETURN “Two or More”
ELSE IF [ERI_AmerInd_AKNatv] = 1 THEN RETURN “A/I AK Native”
ELSE IF [ERI_Asian] = 1 THEN RETURN “Asian”
ELSE IF [ERI_Black_Afr.Amer] = 1 THEN RETURN “Black/AA”
ELSE IF [ERI_HI_PacIsl] = 1 THEN RETURN “Haw/Pac Isl.”
ELSE IF [ERI_White] = 1 THEN RETURN “White”
备注:如果西班牙裔的ERI标志为真(1),则将员工分类为“西班牙裔”
备注:如果超过1个非西班牙裔ERI标志为真,则返回“两个或更多”
** Dataframe **
lname fname rno_cd eri_afr_amer eri_asian eri_hawaiian eri_hispanic eri_nat_amer eri_white rno_defined
0 MOST JEFF E 0 0 0 0 0 1 White
1 CRUISE TOM E 0 0 0 1 0 0 White
2 DEPP JOHNNY 0 0 0 0 0 1 Unknown
3 DICAP LEO 0 0 0 0 0 1 Unknown
4 BRANDO MARLON E 0 0 0 0 0 0 White
5 HANKS TOM 0 0 0 0 0 1 Unknown
6 DENIRO ROBERT E 0 1 0 0 0 1 White
7 PACINO AL E 0 0 0 0 0 1 White
8 WILLIAMS ROBIN E 0 0 1 0 0 0 White
9 EASTWOOD CLINT E 0 0 0 0 0 1 White
8条答案
按热度按时间svdrlsy41#
好的,有两个步骤--第一步是编写一个函数来完成你想要的转换--我已经根据你的伪代码整理了一个例子:
您可能想过一遍,但它似乎起到了作用-注意,进入函数的参数被认为是一个标记为“行”的Series对象。
接下来,在Pandas中使用apply函数来应用函数-例如
注意axis=1说明符,这意味着应用程序是在行级别而不是列级别执行的。结果如下所示:
如果您对这些结果感到满意,那么再次运行它,将结果保存到原始 Dataframe 的新列中。
生成的 Dataframe 如下所示(向右滚动以查看新列):
tcomlyy62#
由于这是第一个谷歌搜索结果'Pandas新专栏从其他',这里有一个简单的例子:
如果你得到了
SettingWithCopyWarning
,你也可以这样做:来源:https://stackoverflow.com/a/12555510/243392
如果列名中包含空格,则可以使用如下语法:
这是apply和assign的文档。
68bkxrlz3#
上面的答案是完全正确的,但是存在一个矢量化的解决方案,其形式为
numpy.select
。这允许您定义条件,然后定义这些条件的输出,这比使用apply
要高效得多:首先,定义条件:
现在,定义相应的输出:
最后,使用
numpy.select
:为什么要使用
numpy.select
而不是apply
?下面是一些性能检查:使用
numpy.select
给我们带来了“巨大”的性能改进,而且这种差异只会随着数据的增长而增加。yc0p9oo04#
.apply()
取函数作为第一个参数;传入label_race
函数,如下所示:你不需要创建一个lambda函数来传入一个函数。
7xllpg7q5#
试试这个,
O/P:
使用
.loc
代替apply
。它改进了矢量化。
.loc
的工作方式很简单,根据条件屏蔽行,将值应用于冻结行。有关详细信息,请访问.loc docs
性能指标:
接受的答案:
1.15 s ± 46.5 ms/循环(7次运行的平均值±标准差,每次运行1个循环)
我的建议答案:
24.7 ms ± 1.7 ms/循环(7次运行的平均值±标准差,每次运行10次循环)
wi3ka0sx6#
如果我们检查它的source code,
apply()
是Python for循环的语法糖(通过FrameApply
类的apply_series_generator()
方法),因为它有panda开销,它通常比Python循环"慢"。尽可能使用优化的(矢量化的)方法。如果你必须使用循环,使用
@numba.jit
装饰器。1.不要将
apply()
用于if-else阶梯df.apply()
几乎是Pandas中最慢的方法。如user3483203和Mohamed Thasin ah的答案所示,根据 Dataframe 大小,np.select()
和df.loc
可能比df.apply()
快50 - 300倍才能产生相同的输出。实际上,使用
numba
模块中的@jit
装饰器的循环实现(与apply()
类似)比df.loc
和np.select
快(大约50 - 60%)。1Numba可以处理numpy数组,所以在使用
jit
装饰器之前,需要将 Dataframe 转换为numpy数组。然后通过检查循环中的条件,在预先初始化的空数组中填充值。由于numpy数组没有列名,你必须通过循环中的索引来访问这些列。if- jitted函数中的else ladders在apply()
中的else ladders上是通过索引访问列的,除此之外几乎是相同的实现。2.不要使用
apply()
进行数值运算如果需要通过添加两列来添加新行,您的第一React可能是编写
但是,使用
sum(axis=1)
方法(如果只有几列,则使用+
运算符)执行按行加法:根据 Dataframe 大小,
sum(1)
可能比apply()
快100倍。事实上,在panda Dataframe 上,几乎不需要使用
apply()
进行数值运算,因为它对大多数运算都进行了优化:加法(sum(1)
),减法(sub()
或diff()
),乘法(prod(1)
),部门(1个一米26英寸1 x或1个一米27英寸1 x),电源(pow()
)、>
、>=
、==
、%
、//
、&
、|
等都可以在没有apply()
的整个 Dataframe 上执行。例如,假设您要使用以下规则创建一个新列:
使用优化的panda方法,这可以写成
等效的
apply()
溶液为:对于20k行的 Dataframe ,使用优化方法的方法比等效的
apply()
方法快250倍。这个差距只会随着数据大小的增加而增加(对于1mil行的 Dataframe ,它快365倍),时间差异将变得越来越明显。21:在下面的结果中,我使用24mil行的 Dataframe (这是我可以在我的机器上构建的最大帧)展示了三种方法的性能。对于较小的帧,numba-jitted函数始终比其他两种方法至少快50%(您可以自己检查)。
二:在下面的结果中,我使用20k行和1mil行的 Dataframe 显示了两种方法的性能。对于较小的帧,差距较小,因为优化的方法有开销,而
apply()
是一个循环。随着帧大小的增加,矢量化开销成本减少了对代码的整个运行时间的影响,而apply()
仍然是帧上的循环。vh0rcniy7#
As @user3483203 pointed out, numpy.select is the best approach
将条件语句和相应的操作存储在两个列表中
You can now use np.select using these lists as its arguments
参考:https://numpy.org/doc/stable/reference/generated/numpy.select.html
kt06eoxx8#
还有另一种(易于推广的)方法,其基石是
pandas.DataFrame.idxmax
。...最后,在 vectorized fashion 中:
其中
这是一个
pandas.Series
示例,您可以轻松地将其托管在df
中,即执行df['race_label'] = race_label
。