使用 np.vectorize 在数据框中创建列
Using np.vectorize to create a column in a data frame
我有一个数据框,其中包含两列数字和第三列重复字母。让我们这样说:
import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.randint(0,100,size=(100, 2)), columns=list('xy'))
letters = ['A', 'B', 'C', 'D'] * int(len(df.index) / 4)
df['letters'] = letters
我想创建两个新列,将 'x' 和 'y' 列中的数字与其对应字母的平均值进行比较。例如,一个新列将只包含数字 10(如果比平均值低 20% 或更好)、-10(如果比平均值低 20%)或者 0。
我写了下面的函数:
def scoreFunHigh(dataField, mean, diff, multip):
upper = mean * (1 + diff)
lower = mean * (1 - diff)
if dataField > upper:
return multip * 1
elif dataField < lower:
return multip * (-1)
else:
return 0
然后创建列如下:
letterMeanX = df.groupby('letters')['x'].transform(np.nanmean)
df['letter x score'] = np.vectorize(scoreFunHigh)(df['x'], letterMeanX, 0.2, 10)
letterMeanY = df.groupby('letters')['y'].transform(np.nanmean)
df['letter y score'] = np.vectorize(scoreFunHigh)(df['y'], letterMeanY, 0.3, 5)
这行得通。但是,我收到以下运行时警告:
C:\Users\ ... \Python\Python38\lib\site-packages\numpy\lib\function_base.py:2167: RuntimeWarning: 在中遇到无效值? (矢量化)
输出 = ufunc(*输入)
(请注意,如果我 运行 与上面的代码完全相同,我不会收到错误。我的真实数据框要大得多,我正在为不同的数据使用多个函数)
这里有什么问题?有没有更好的设置方法?
非常感谢
这里是不使用的版本 np.vectorize
def scoreFunHigh(val, mean, diff, multip):
upper = mean * (1 + diff)
lower = mean * (1 - diff)
if val > upper:
return multip * 1
elif val < lower:
return multip * (-1)
else:
return 0
letterMeanX = df.groupby('letters')['x'].apply(lambda x: np.nanmean(x))
df['letter x score'] = df.apply(
lambda row: scoreFunHigh(row['x'], letterMeanX[row['letters']], 0.2, 10), axis=1)
输出
x y letters letter x score
0 52 76 A 0
1 90 99 B 10
2 87 43 C 10
3 44 73 D 0
4 49 3 A 0
.. .. .. ... ...
95 16 51 D -10
96 38 3 A 0
97 43 47 B 0
98 58 39 C 0
99 41 26 D 0
您提供的示例没有产生运行时警告,因此我们无法帮助您进行诊断。我不知道更完整的追溯是否提供了任何有用的信息。
但是让我们看看计算结果:
In [70]: np.vectorize(scoreFunHigh)(df['x'], letterMeanX, 0.2, 10)
Out[70]:
array([-10, 0, 10, -10, 0, 0, -10, -10, 10, 0, 0, 10, -10,
-10, 0, 10, 10, -10, 0, 10, -10, -10, -10, 10, 10, -10,
...
-10, 10, -10, 0, 0, 10, 10, 0, 10])
以及 df 赋值:
In [74]: df['letter x score'] = np.vectorize(scoreFunHigh)(df['x'], letterMeanX,
...: 0.2, 10)
...:
In [75]: df
Out[75]:
x y letters letter x score
0 33 98 A -10
1 38 49 B 0
2 78 46 C 10
3 31 46 D -10
4 41 74 A 0
.. .. .. ... ...
95 51 4 D 0
96 70 4 A 10
97 74 74 B 10
98 54 70 C 0
99 87 44 D 10
经常 np.vectorize
由于 otypes
问题而出现问题(阅读文档);如果试验计算产生一个整数,则 return dtype 设置为那个,如果其他值是浮点数,则会出现问题。但是在这种情况下,结果只能具有三个值之一,[-10,0,10](最后一个参数)。
您提供的警告表明较大数据框中的某些值对于您的 scoreFunHigh
函数中的计算是错误的。但是警告没有提供足够的细节来说明什么。
将真正的 numpy 向量化应用于此问题相对容易,因为它依赖于两个系列,df['x]
一个 letterMeanX
和 2 个标量。
In [111]: letterMeanX = df.groupby('letters')['x'].transform(np.nanmean)
In [112]: letterMeanX.shape
Out[112]: (100,)
In [113]: df['x'].shape
Out[113]: (100,)
In [114]: upper = letterMeanX *(1+0.2)
In [115]: lower = letterMeanX *(1-0.2)
In [116]: res = np.zeros(letterMeanX.shape,int)
In [117]: res[df['x']>upper] = 10
In [118]: res[df['x']<lower] = -10
In [119]: np.allclose(res, Out[70])
Out[119]: True
换句话说,不是逐行应用 upper/lower 比较,而是将其应用于整个系列。它仍在迭代,但在编译的 numpy 方法中,速度要快得多。 np.vectorize
只是一个迭代的包装器。它仍然会为每一行调用一次 python 函数。希望性能免责声明足够清楚。
考虑直接调用您的函数,稍微调整方法以使用 numpy.select
(or numpy.where
) 处理条件逻辑。使用这种方法,没有循环 运行 而是对 Series 和标量参数的矢量化操作:
def scoreFunHigh(dataField, mean, diff, multip):
conds = [dataField > mean * (1 + diff),
dataField < mean * (1 - diff)]
vals = [multip * 1, multip * (-1)]
return np.select(conds, vals, default=0)
letterMeanX = df.groupby('letters')['x'].transform(np.nanmean)
df['letter x score'] = scoreFunHigh(df['x'], letterMeanX, 0.2, 10)
letterMeanY = df.groupby('letters')['y'].transform(np.nanmean)
df['letter y score'] = scoreFunHigh(df['y'], letterMeanY, 0.3, 5)
我有一个数据框,其中包含两列数字和第三列重复字母。让我们这样说:
import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.randint(0,100,size=(100, 2)), columns=list('xy'))
letters = ['A', 'B', 'C', 'D'] * int(len(df.index) / 4)
df['letters'] = letters
我想创建两个新列,将 'x' 和 'y' 列中的数字与其对应字母的平均值进行比较。例如,一个新列将只包含数字 10(如果比平均值低 20% 或更好)、-10(如果比平均值低 20%)或者 0。
我写了下面的函数:
def scoreFunHigh(dataField, mean, diff, multip):
upper = mean * (1 + diff)
lower = mean * (1 - diff)
if dataField > upper:
return multip * 1
elif dataField < lower:
return multip * (-1)
else:
return 0
然后创建列如下:
letterMeanX = df.groupby('letters')['x'].transform(np.nanmean)
df['letter x score'] = np.vectorize(scoreFunHigh)(df['x'], letterMeanX, 0.2, 10)
letterMeanY = df.groupby('letters')['y'].transform(np.nanmean)
df['letter y score'] = np.vectorize(scoreFunHigh)(df['y'], letterMeanY, 0.3, 5)
这行得通。但是,我收到以下运行时警告:
C:\Users\ ... \Python\Python38\lib\site-packages\numpy\lib\function_base.py:2167: RuntimeWarning: 在中遇到无效值? (矢量化) 输出 = ufunc(*输入)
(请注意,如果我 运行 与上面的代码完全相同,我不会收到错误。我的真实数据框要大得多,我正在为不同的数据使用多个函数)
这里有什么问题?有没有更好的设置方法?
非常感谢
这里是不使用的版本 np.vectorize
def scoreFunHigh(val, mean, diff, multip):
upper = mean * (1 + diff)
lower = mean * (1 - diff)
if val > upper:
return multip * 1
elif val < lower:
return multip * (-1)
else:
return 0
letterMeanX = df.groupby('letters')['x'].apply(lambda x: np.nanmean(x))
df['letter x score'] = df.apply(
lambda row: scoreFunHigh(row['x'], letterMeanX[row['letters']], 0.2, 10), axis=1)
输出
x y letters letter x score
0 52 76 A 0
1 90 99 B 10
2 87 43 C 10
3 44 73 D 0
4 49 3 A 0
.. .. .. ... ...
95 16 51 D -10
96 38 3 A 0
97 43 47 B 0
98 58 39 C 0
99 41 26 D 0
您提供的示例没有产生运行时警告,因此我们无法帮助您进行诊断。我不知道更完整的追溯是否提供了任何有用的信息。
但是让我们看看计算结果:
In [70]: np.vectorize(scoreFunHigh)(df['x'], letterMeanX, 0.2, 10)
Out[70]:
array([-10, 0, 10, -10, 0, 0, -10, -10, 10, 0, 0, 10, -10,
-10, 0, 10, 10, -10, 0, 10, -10, -10, -10, 10, 10, -10,
...
-10, 10, -10, 0, 0, 10, 10, 0, 10])
以及 df 赋值:
In [74]: df['letter x score'] = np.vectorize(scoreFunHigh)(df['x'], letterMeanX,
...: 0.2, 10)
...:
In [75]: df
Out[75]:
x y letters letter x score
0 33 98 A -10
1 38 49 B 0
2 78 46 C 10
3 31 46 D -10
4 41 74 A 0
.. .. .. ... ...
95 51 4 D 0
96 70 4 A 10
97 74 74 B 10
98 54 70 C 0
99 87 44 D 10
经常 np.vectorize
由于 otypes
问题而出现问题(阅读文档);如果试验计算产生一个整数,则 return dtype 设置为那个,如果其他值是浮点数,则会出现问题。但是在这种情况下,结果只能具有三个值之一,[-10,0,10](最后一个参数)。
您提供的警告表明较大数据框中的某些值对于您的 scoreFunHigh
函数中的计算是错误的。但是警告没有提供足够的细节来说明什么。
将真正的 numpy 向量化应用于此问题相对容易,因为它依赖于两个系列,df['x]
一个 letterMeanX
和 2 个标量。
In [111]: letterMeanX = df.groupby('letters')['x'].transform(np.nanmean)
In [112]: letterMeanX.shape
Out[112]: (100,)
In [113]: df['x'].shape
Out[113]: (100,)
In [114]: upper = letterMeanX *(1+0.2)
In [115]: lower = letterMeanX *(1-0.2)
In [116]: res = np.zeros(letterMeanX.shape,int)
In [117]: res[df['x']>upper] = 10
In [118]: res[df['x']<lower] = -10
In [119]: np.allclose(res, Out[70])
Out[119]: True
换句话说,不是逐行应用 upper/lower 比较,而是将其应用于整个系列。它仍在迭代,但在编译的 numpy 方法中,速度要快得多。 np.vectorize
只是一个迭代的包装器。它仍然会为每一行调用一次 python 函数。希望性能免责声明足够清楚。
考虑直接调用您的函数,稍微调整方法以使用 numpy.select
(or numpy.where
) 处理条件逻辑。使用这种方法,没有循环 运行 而是对 Series 和标量参数的矢量化操作:
def scoreFunHigh(dataField, mean, diff, multip):
conds = [dataField > mean * (1 + diff),
dataField < mean * (1 - diff)]
vals = [multip * 1, multip * (-1)]
return np.select(conds, vals, default=0)
letterMeanX = df.groupby('letters')['x'].transform(np.nanmean)
df['letter x score'] = scoreFunHigh(df['x'], letterMeanX, 0.2, 10)
letterMeanY = df.groupby('letters')['y'].transform(np.nanmean)
df['letter y score'] = scoreFunHigh(df['y'], letterMeanY, 0.3, 5)