使用 np.vectorize 在数据框中创建列



>我有一个数据框架,其中包含两列数字和第三列重复字母。让我们这样说:

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-packagesumpy\lib\function_base.py:2167:运行时警告:在 中遇到无效值?(矢量化( 输出 = ufunc(*输入(

(请注意,如果我运行与上面完全相同的代码,则不会收到错误。我的实际数据帧要大得多,并且我正在为不同的数据使用多个函数(

这里有什么问题?有没有更好的方法来设置它?

谢谢

您提供的示例不会生成运行时警告,因此我们无法执行任何操作来帮助您诊断它。 我不知道更全面的回溯是否提供了任何有用的信息。

但是让我们看看计算:

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问题而出现问题(阅读文档(; 如果试验计算生成一个整数,则返回 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

换句话说,它不是逐行应用上/下比较,而是将其应用于整个系列。 它仍在迭代,但在编译的numpy方法中,速度要快得多。np.vectorize只是迭代的包装器。 它仍然为每一行调用一次你的 python 函数。 希望性能免责声明足够清楚。

考虑直接调用您的函数,并稍微调整方法以使用numpy.select(或numpy.where(处理条件逻辑。使用这种方法,不会运行循环,而是对序列和标量参数进行矢量化操作:

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)

这是不使用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

最新更新