如何消除 Pandas Dataframe 中基于多个 if、elif 语句填充列的每一行值的循环
How to Eliminate for loop in Pandas Dataframe in filling each row values of a column based on multiple if,elif statements
尝试摆脱 for 循环,以加快根据涉及多列和多行的 if、elif 条件填充列 'C' 中的值的执行速度。无法找到合适的解决方案。
尝试应用 np.where 条件、选项和默认值。但未能获得预期结果,因为我无法从 pandas 系列对象中提取单个值。
df = pd.DataFrame()
df['A']=['Yes','Yes','No','No','Yes','No','Yes','Yes','Yes','Yes']
df['B']=[1,1,0,1,1,0,1,0,0,1]
df['C']=None
df['D']=['xyz','Yes','No','xyz','Yes','No','xyz','Yes','Yes','Yes']
df['C'][0]='xyz'
for i in range(0,len(df)-1):
if (df.iloc[1+i, 1]==1) & (df.iloc[i, 2]=="xyz") & (df.iloc[1+i, 0]=="No"):
df.iloc[1+i, 2] = "Minus"
elif (df.iloc[1+i, 1]==1) & (df.iloc[i, 2]=="xyz") & (df.iloc[1+i, 0]=="Yes"):
df.iloc[1+i, 2] = "Plus"
elif (df.iloc[i, 3]!="xyz") or ((df.iloc[1+i, 1]==0) & (df.iloc[i, 2]=="xyz")):
df.iloc[1+i, 2] = "xyz"
elif (df.iloc[1+i, 0]=="Yes") & (df.iloc[i, 2]=="xyz"):
df.iloc[1+i, 2] = "Plus"
elif (df.iloc[1+i, 0]=="No") & (df.iloc[i, 2]=="xyz"):
df.iloc[1+i, 2] = "Minus"
else:
df.iloc[1+i, 2] = df.iloc[i, 2]
df
期待社区的帮助,将上述代码修改为执行时间更短的更好的代码。最好使用 numpy 向量化。
当然不能使用 Numpy 或 Pandas 对循环进行有效矢量化,因为 循环携带的数据依赖于 df['C']
。由于 Pandas 直接索引和字符串比较,循环非常慢。希望您可以使用 Numba 有效地解决这个问题。您首先需要将列转换为 strongly-typed Numpy 数组,以便 Numba 有用。请注意,Numba 处理字符串的速度非常慢,因此最好直接使用 Numpy 执行矢量化检查。
这是结果代码:
import numpy as np
import numba as nb
@nb.njit('UnicodeCharSeq(8)[:](bool_[:], int64[:], bool_[:])')
def compute(a, b, d):
n = a.size
c = np.empty(n, dtype='U8')
c[0] = 'xyz'
for i in range(0, n-1):
prev_is_xyz = c[i] == 'xyz'
if b[i+1]==1 and prev_is_xyz and not a[i+1]:
c[i+1] = 'Minus'
elif b[i+1]==1 and prev_is_xyz and a[i+1]:
c[i+1] = 'Plus'
elif d[i] or (b[i+1]==0 and prev_is_xyz):
c[i+1] = 'xyz'
elif a[i+1] and prev_is_xyz:
c[i+1] = 'Plus'
elif not a[i+1] and prev_is_xyz:
c[i+1] = 'Minus'
else:
c[i+1] = c[i]
return c
# Convert the dataframe columns to fast Numpy arrays and precompute some check
a = df['A'].values.astype('U8') == 'Yes'
b = df['B'].values.astype(np.int64)
d = df['D'].values.astype('U8') != 'xyz'
# Compute the result very quickly with Numba
c = compute(a, b, d)
# Store the result back
df['C'].values[:] = c.astype(object)
这是我机器上的最终性能:
Basic Pandas loops: 2510 us
This Numba code: 20 us
因此,Numba 实施速度 125 倍。事实上,大部分时间都花在了 Numpy 转换代码上,甚至没有花在 compute
上。在大型数据帧上差距应该更大。
请注意,行 df['C'].values[:] = c.astype(object)
比等效表达式 df['C'] = c
快得多(大约 16 倍)。
尝试摆脱 for 循环,以加快根据涉及多列和多行的 if、elif 条件填充列 'C' 中的值的执行速度。无法找到合适的解决方案。
尝试应用 np.where 条件、选项和默认值。但未能获得预期结果,因为我无法从 pandas 系列对象中提取单个值。
df = pd.DataFrame()
df['A']=['Yes','Yes','No','No','Yes','No','Yes','Yes','Yes','Yes']
df['B']=[1,1,0,1,1,0,1,0,0,1]
df['C']=None
df['D']=['xyz','Yes','No','xyz','Yes','No','xyz','Yes','Yes','Yes']
df['C'][0]='xyz'
for i in range(0,len(df)-1):
if (df.iloc[1+i, 1]==1) & (df.iloc[i, 2]=="xyz") & (df.iloc[1+i, 0]=="No"):
df.iloc[1+i, 2] = "Minus"
elif (df.iloc[1+i, 1]==1) & (df.iloc[i, 2]=="xyz") & (df.iloc[1+i, 0]=="Yes"):
df.iloc[1+i, 2] = "Plus"
elif (df.iloc[i, 3]!="xyz") or ((df.iloc[1+i, 1]==0) & (df.iloc[i, 2]=="xyz")):
df.iloc[1+i, 2] = "xyz"
elif (df.iloc[1+i, 0]=="Yes") & (df.iloc[i, 2]=="xyz"):
df.iloc[1+i, 2] = "Plus"
elif (df.iloc[1+i, 0]=="No") & (df.iloc[i, 2]=="xyz"):
df.iloc[1+i, 2] = "Minus"
else:
df.iloc[1+i, 2] = df.iloc[i, 2]
df
期待社区的帮助,将上述代码修改为执行时间更短的更好的代码。最好使用 numpy 向量化。
当然不能使用 Numpy 或 Pandas 对循环进行有效矢量化,因为 循环携带的数据依赖于 df['C']
。由于 Pandas 直接索引和字符串比较,循环非常慢。希望您可以使用 Numba 有效地解决这个问题。您首先需要将列转换为 strongly-typed Numpy 数组,以便 Numba 有用。请注意,Numba 处理字符串的速度非常慢,因此最好直接使用 Numpy 执行矢量化检查。
这是结果代码:
import numpy as np
import numba as nb
@nb.njit('UnicodeCharSeq(8)[:](bool_[:], int64[:], bool_[:])')
def compute(a, b, d):
n = a.size
c = np.empty(n, dtype='U8')
c[0] = 'xyz'
for i in range(0, n-1):
prev_is_xyz = c[i] == 'xyz'
if b[i+1]==1 and prev_is_xyz and not a[i+1]:
c[i+1] = 'Minus'
elif b[i+1]==1 and prev_is_xyz and a[i+1]:
c[i+1] = 'Plus'
elif d[i] or (b[i+1]==0 and prev_is_xyz):
c[i+1] = 'xyz'
elif a[i+1] and prev_is_xyz:
c[i+1] = 'Plus'
elif not a[i+1] and prev_is_xyz:
c[i+1] = 'Minus'
else:
c[i+1] = c[i]
return c
# Convert the dataframe columns to fast Numpy arrays and precompute some check
a = df['A'].values.astype('U8') == 'Yes'
b = df['B'].values.astype(np.int64)
d = df['D'].values.astype('U8') != 'xyz'
# Compute the result very quickly with Numba
c = compute(a, b, d)
# Store the result back
df['C'].values[:] = c.astype(object)
这是我机器上的最终性能:
Basic Pandas loops: 2510 us
This Numba code: 20 us
因此,Numba 实施速度 125 倍。事实上,大部分时间都花在了 Numpy 转换代码上,甚至没有花在 compute
上。在大型数据帧上差距应该更大。
请注意,行 df['C'].values[:] = c.astype(object)
比等效表达式 df['C'] = c
快得多(大约 16 倍)。