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

2024-06-24 12:06:54 发布

您现在位置:Python中文网/ 问答频道 /正文

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

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\u base.py:2167:RuntimeWarning:在中遇到无效值?(矢量化) 输出=ufunc(*输入)

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

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

多谢各位


Tags: 数据importdfreturnasnp字母diff
3条回答
<>考虑直接调用函数,对方法进行轻微调整,使用{a1}(或^{})处理条件逻辑。使用这种方法,不运行循环,而是对级数和标量参数执行矢量化操作:

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

您提供的示例不会生成runtimewarning,因此我们无法帮助您诊断它。我不知道更全面的追踪是否能提供有用的信息

但让我们看看计算结果:

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问题而产生问题(阅读文档);如果试算生成一个整数,那么返回的数据类型将设置为该整数,如果其他值为浮点值,则会出现问题。但是在这种情况下,结果只能有三个值中的一个,[-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函数。希望性能免责声明足够清楚

相关问题 更多 >