矢量化操作来实现apply功能,提高计算效率

90 阅读3分钟
import pandas as pd

# 创建示例数据
data = {
    'itemId': ['A1', 'A2', 'A3', 'B1', 'B2', 'C1', 'C2', 'C3', 'D1', 'D2'],
    'click_': [0.12, 0.15, 0.20, 0.05, 0.10, 0.25, 0.30, 0.35, 0.08, 0.18],
    'category': ['Electronics', 'Electronics', 'Electronics', 'Books', 'Books', 
                 'Clothing', 'Clothing', 'Clothing', 'Home', 'Home'],
    'vol':[1,10,15,21,2,16,8,5,17,90]
}

# 创建数据框
df = pd.DataFrame(data)

# 显示数据框
display(df)
df['click_avg']=df.groupby('category')['click_'].transform('mean')
df['gt_avg']=df['click_']>df['click_avg']
def vol_fix(row):
    vol=row['vol']
    gt=row['gt_avg']
    if vol<=10:
        row['vol_new']=vol
    else:
        if gt:
            row['vol_new']= vol+np.random.randint(0,10)
        else:
            row['vol_new']= vol-np.random.randint(0,10)
    
    return row  
    
df=df.apply(vol_fix,axis=1) 

display(df)

要提高代码的效率,可以考虑避免在 apply 函数中使用循环和条件判断。我们可以通过矢量化操作来实现同样的逻辑,这样可以充分利用 Pandas 的性能优化。

以下是改进后的代码:

import pandas as pd
import numpy as np

# 创建示例数据
data = {
    'itemId': ['A1', 'A2', 'A3', 'B1', 'B2', 'C1', 'C2', 'C3', 'D1', 'D2'],
    'click_rate': [0.12, 0.15, 0.20, 0.05, 0.10, 0.25, 0.30, 0.35, 0.08, 0.18],
    'category': ['Electronics', 'Electronics', 'Electronics', 'Books', 'Books', 
                 'Clothing', 'Clothing', 'Clothing', 'Home', 'Home'],
    'vol': [1, 10, 15, 21, 2, 16, 8, 5, 17, 90]
}

# 创建数据框
df = pd.DataFrame(data)

# 计算每个品类的平均点击率并添加到新列
df['click_rate_avg'] = df.groupby('category')['click_rate'].transform('mean')

# 添加一列表示点击率是否大于平均点击率
df['gt_avg'] = df['click_rate'] > df['click_rate_avg']

# 使用矢量化操作计算新的 vol 值
# 对于 vol <= 10 的行,vol_new 等于原始 vol
df['vol_new'] = df['vol']

# 对于 gt_avg 为 True 且 vol > 10 的行,vol_new 增加随机值
df.loc[(df['gt_avg'] == True) & (df['vol'] > 10), 'vol_new'] += np.random.randint(0, 10, df.loc[(df['gt_avg'] == True) & (df['vol'] > 10), 'vol_new'].shape[0])

# 对于 gt_avg 为 False 且 vol > 10 的行,vol_new 减少随机值
df.loc[(df['gt_avg'] == False) & (df['vol'] > 10), 'vol_new'] -= np.random.randint(0, 10, df.loc[(df['gt_avg'] == False) & (df['vol'] > 10), 'vol_new'].shape[0])

# 显示数据框
display(df)

解释:

  1. 创建示例数据框:包含 itemIdclick_ratecategoryvol 列。
  2. 计算每个品类的平均点击率:使用 groupbytransform('mean') 计算每个品类的平均点击率,并将结果添加到 click_rate_avg 列。
  3. 添加比较列:创建一个新列 gt_avg,表示 click_rate 是否大于 click_rate_avg
  4. 使用矢量化操作计算新的 vol
    • 对于 vol 小于等于 10 的行,vol_new 等于原始 vol
    • 对于 gt_avgTruevol 大于 10 的行,vol_new 增加一个随机值。
    • 对于 gt_avgFalsevol 大于 10 的行,vol_new 减少一个随机值。
  5. 显示结果:使用 display(df) 显示数据框。

这种方法避免了使用 apply,大大提高了代码的执行效率。

df.loc[(df['gt_avg'] == True) & (df['vol'] > 10), 'vol_new'].shape[0]

解释df.loc[(df['gt_avg'] == True) & (df['vol'] > 10), 'vol_new'].shape[0]:

  1. df.loc[...]:这是 Pandas 的 .loc 索引器,用于按标签或布尔条件进行访问。这里我们使用布尔条件选择满足特定条件的行。

  2. 条件 df['gt_avg'] == True

    • df['gt_avg'] 是一个布尔 Series,表示 click_rate 是否大于 click_rate_avg
    • == True 是显式检查,但可以简化为 df['gt_avg'],因为布尔 Series 本身就表示 TrueFalse
  3. 条件 df['vol'] > 10

    • 这是一个布尔条件,表示 vol 大于 10。
  4. 组合条件 (df['gt_avg'] == True) & (df['vol'] > 10)

    • 使用按位与运算符 & 将两个布尔条件组合,选择满足这两个条件的行。
  5. 选择 vol_new

    • df.loc[... , 'vol_new'] 选择满足条件的行的 vol_new 列。
  6. .shape[0]

    • .shape 属性返回数据框或 Series 的维度。对于 Series(或一维数组),shape 是一个包含行数的元组。
    • .shape[0] 获取行数,即满足条件的行数。

这部分代码的目的是计算满足条件的行数,以便生成相同数量的随机数,从而在矢量化操作中更新 vol_new 列。例如:

import pandas as np

# 对于 gt_avg 为 True 且 vol > 10 的行,vol_new 增加随机值
num_rows = df.loc[(df['gt_avg'] == True) & (df['vol'] > 10), 'vol_new'].shape[0]
df.loc[(df['gt_avg'] == True) & (df['vol'] > 10), 'vol_new'] += np.random.randint(0, 10, num_rows)