【问题标题】:Product of masked dataframe掩码数据框的乘积
【发布时间】:2017-05-11 18:38:21
【问题描述】:

我有一个包含超过 600 万个观察值的数据框,其中 20 列是权重,将应用于单个得分列。即,Wgt1 * Wgt2 * Wgt3...* 得分。此外,并非每个权重都适用于每个观察,因此我创建了 20 个代表权重掩码的列。即 (Wgt1*Msk1) * (Wgt2*Msk2) * (Wgt3*Msk3) ... 得分。当掩码为0时,权重不适用;当掩码为1时,适用。

对于数据框中的每一行,我想: 1、检查2个表明该行应该被处理的限定条件 2、求权重的乘积,以对应掩码(ttl_wgt)的存在为准 3、将此乘积乘以分数(prob),得出最终加权分数

为此,我创建了一个用户定义函数:

import functools
import operator
import time    
def mymult(a):
        ttl_wgt = float('NaN') #Initialize to NaN
        if ~np.isnan(a['ID']): #condition 1, only process if an ID is present
            if a['prob'] > -1.0: #condition 2, only process if our unweighted score is NOT -1.0
                b = np.where(a[msks] ==1)[0] #index for which of our masks is 1?
                ttl_wgt = functools.reduce(operator.mul, a[np.asarray(wgt_nms)[b]], 1)
        return ttl_wgt

我在开发过程中内存不足,所以我决定一次将它分成 500000 行。我使用 lambda 函数来应用到块:

msks = ['Msk1','Msk2','Msk3','Msk4',...,'Msk20']
wgt_nms = ['Wgt1','Wgt2','Wgt3','Wgt4',...,'Wgt20']
print('Determining final weights...')
chunksize = 500000 #we'll operate on this many rows at a time
start_time = time.time()
ttl_wgts = [] #initialize list to hold weight products
for i in range(0,len(df),chunksize): 
    ttl_wgts.extend(df[i:(i+chunksize)].apply(lambda x: mymult(x), axis=1))
print("--- %s seconds ---" % (time.time() - start_time)) #Expect between 30 and 40 minutes
print('Done!')

然后我将 ttl_wgts 列表分配为数据框中的新列,并做权重 * 初始分数的最终乘积。

#Initialize the fields
#Might not be necessary or evenuseful
df['ttl_wgt'] = float('NaN')
df['wgt_prob'] = float('NaN')

df['ttl_wgt'] = ttl_wgts
df['wgt_prob'] = df['ttl_wgt'] * df['prob']

我在multiplying elements in a list 上查看了之前的帖子。这是值得深思的好东西,但我无法将其转化为对我 600 万以上的观察更有效的东西。我还应该考虑其他方法吗?

按照建议添加示例 df

数据框的样本可能看起来像这样,只有 3 个掩码/权重:

df = pd.DataFrame({'id': [999999999,136550,80010170,80010177,90002408,90002664,16207501,62992,np.nan,80010152], 
                   'prob': [-1,0.180274382,0.448361456,0.000945058,0.005060279,0.009893078,0.169686288,0.109541453,0.117907763,0.266242921],
                   'Msk1': [0,1,1,1,0,0,1,0,0,0],
                   'Msk2': [0,0,1,0,0,0,0,1,0,0],
                   'Msk3': [1,0,0,0,1,1,0,0,1,1],
                   'Wgt1': [np.nan,0.919921875,1.08984375,1.049804688,np.nan,np.nan,np.nan,0.91015625,np.nan,0.810058594],
                   'Wgt2': [np.nan,1.129882813,1.120117188,0.970214844,np.nan,np.nan,np.nan,1.0703125,np.nan,0.859863281],
                   'Wgt3': [np.nan,1.209960938,1.23046875,1,np.nan,np.nan,np.nan,1.150390625,np.nan,0.649902344]
                   })

在第一次观察中,prob 字段为 -1,因此不会处理该行。在第二个观察中,Msk1 打开,而 Msk2 和 Msk3 关闭。因此,最终权重将是 Wgt1 = 0.919922 的值。在第 3 行,Mask1 和 Msk2 处于打开状态,而 Msk3 处于关闭状态。因此最终权重为 Wgt1*Wgt2 = 1.089844*1.120117 = 1.220752。

【问题讨论】:

  • 这是一个非常令人困惑的问题,如果您提供一个非常小的数据子集并提供所需的结果会更好。了解如何制作good reproducible pandas example
  • 这个问题已经有点长了,所以我来回包括一个例子。感谢您的提醒,我添加了一个希望能澄清情况。

标签: python pandas dataframe lambda iteration


【解决方案1】:

IIUC:

您想用 1 填充掩码权重。然后您可以将它们全部相乘,而不会受到被掩码的权重的影响。这就是诀窍。您必须根据需要应用它。

创建msk

msk = df.filter(like='Msk')
print(msk)

   Msk1  Msk2  Msk3
0     0     0     1
1     1     0     0
2     1     1     0
3     1     0     0
4     0     0     1
5     0     0     1
6     1     0     0
7     0     1     0
8     0     0     1
9     0     0     1

创建wgt

wgt = df.filter(like='Wgt')
print(wgt)

       Wgt1      Wgt2      Wgt3
0       NaN       NaN       NaN
1  0.919922  1.129883  1.209961
2  1.089844  1.120117  1.230469
3  1.049805  0.970215  1.000000
4       NaN       NaN       NaN
5       NaN       NaN       NaN
6       NaN       NaN       NaN
7  0.910156  1.070312  1.150391
8       NaN       NaN       NaN
9  0.810059  0.859863  0.649902

创建new_weight

new_wgt = np.where(msk, wgt, 1)
print(new_wgt)

[[ 1.          1.                 nan]
 [ 0.91992188  1.          1.        ]
 [ 1.08984375  1.12011719  1.        ]
 [ 1.04980469  1.          1.        ]
 [ 1.          1.                 nan]
 [ 1.          1.                 nan]
 [        nan  1.          1.        ]
 [ 1.          1.0703125   1.        ]
 [ 1.          1.                 nan]
 [ 1.          1.          0.64990234]]

最终prod_wgt

prod_wgt = pd.Series(new_wgt.prod(1), wgt.index)
print(prod_wgt)

0         NaN
1    0.919922
2    1.220753
3    1.049805
4         NaN
5         NaN
6         NaN
7    1.070312
8         NaN
9    0.649902
dtype: float64

【讨论】:

  • 我没有想过通过 numpy.where 将掩码作为条件传递给权重。但现在它完全有道理。这将我约 30 分钟的时间缩短到
猜你喜欢
  • 2018-12-29
  • 1970-01-01
  • 1970-01-01
  • 2013-07-07
  • 2021-02-25
  • 1970-01-01
  • 1970-01-01
  • 2017-11-10
  • 1970-01-01
相关资源
最近更新 更多