Pandas矢量化:基于JSON个文件的累计和

Pandas vectorization: Cumulative sum based on JSON files

我正在尝试根据 DataFrame 和两个 json 文件中的值求和一个分数。我有一个最小的例子和一个最小的解决方案,但这需要以某种方式进行矢量化,因为在实际情况下有超过一百万行,运行 通过 1% 的行需要大约 40 分钟。

我的 first.json- 文件是:

{
    "variables" : {
        "var_1": {
            "values": [
                {
                    "lb": 1.0,
                    "b_cumul": 0.04
                },
                {
                    "lb": 3.0,
                    "b_cumul": 0.28
                }
            ]
        },
        "var_2": {
            "values": [
                {
                    "lb": 0,
                    "b_cumul": -0.09
                },
                {
                    "lb": 1,
                    "b_cumul": 0.14
                },
                {
                    "lb:": 4,
                    "b_cumul": 0.03
                }
            ]
        },
        "var_4": {
            "values": [
                {
                    "lb": "1",
                    "b_cumul": 0.06
                }
            ]
        }
    }
}

我的 second.json 文件是:

{
    "variables" : {
        "var_1": {
            "values": [
                {
                    "lb": 1.0,
                    "b_cumul": -0.15
                },
                {
                    "lb": 2.0,
                    "b_cumul": 0.06
                },
                {
                    "lb": 4.0,
                    "b_cumul": 0.02
                },
                {
                    "lb": 5.0,
                    "b_cumul": 0.15
                }
            ]
        },
        "var_3": {
            "values": [
                {
                    "lb": 0.0,
                    "b_cumul": 0.12
                },
                {
                    "lb": 2.0,
                    "b_cumul": 0.25
                }
            ]
        },
        "var_6": {
            "values": [
                {
                    "lb": 0.0,
                    "b_cumul": -0.16
                },
                {
                    "lb": 1.0,
                    "b_cumul": -0.06
                }
            ]
        }
    }
}

这是我们的初始数据框:

import pandas as pd
# setup initial test-data
usage = ['first', 'second', 'first', 'second', 'second', 'second', 'first']
var_1 = [-1, -1, 0, 1, 3, 8, 2]
var_2 = [1, 3, -1, 0, 9, 2, 1]
var_3 = [0, 1, 0, -1, -1, 42, 3]
df = pd.DataFrame({'usage': usage, 'var_1': var_1, 'var_2': var_2, 'var_3': var_3})

var_1var_2var_3 df 中可用的变量决定了我们要在 json- 中查看哪些变量文件。分数应该是从 json 文件中检索到的累积总和,具体取决于 df.

中的值

查看我的第一行,我有 (var_1=-1, var_2=1, var_3=0)。由于 usage='first' 对于同一行,我需要检查 first.json 这些变量对应的分数。 var_3first.json 中不存在,所以这个变量给出 0 分。 var_1=-1,所以这也给出了 0 分。 var_2=1,所以这里我们需要查看 first.json 并获取对应于 <=1 的值的分数,在本例中为 -0.09+0.14=0.05。所以我们想通过 df.loc[0, 'score_sum']=0+0-0.09+0.14.

在数据框中添加此信息

我已经用下面的代码解决了这个问题,但是如前所述,这是非常低效的,并且不适用于更大的 df

import seaborn as sns
from matplotlib.pyplot import plt
import json

# return score based on given value and score ranges
def calculate_score(value: Number, score_ranges: pd.DataFrame) -> Number:
    score_ranges.lb = pd.to_numeric(score_ranges.lb)
    if score_ranges.lb.min() > value:
        return 0
    score_sum = score_ranges.loc[(score_ranges.lb <= value), 'b_cumul'].sum()
    return score_sum

# read relevant data from json files
models = {key: [] for key in df['usage'].unique()}
for path in models:
    f = open(f"{path}.json")
    all_variables = json.load(f)['variables']
    relevant_variables = [x for x in df.columns if x in all_variables]
    for var in relevant_variables:
        models[path].append({var: all_variables[var]['values']})

# calculate scores
df['score_sum'] = np.nan
for index, row in df.iterrows():
    score = 0
    m = row['usage']
    for var in models[m]:
        var_name = list(var)[0]
        value = row[var_name] 
        if value == -1:
            score += 0
        elif value > -1:
            score += calculate_score(value, pd.DataFrame(var[var_name]))
    df.loc[index, 'score_sum'] = score

通过 运行 执行此代码然后打印 df,我们注意到第一行具有预期的 score_sum=0.05。我们在顶部 import seaborn, plt 因为我们想在最后 运行 sns.distplot(df['score_sum']) 并保存图形。

编辑: 根据要求,请参阅下面的总结果 DataFrame 的屏幕截图。澄清一下:对于第二行,usage='second' 这意味着我们使用 second.json,但是我们在这个 json 中没有 var_2,所以 var_2=3 将只需添加 score_sum+=0,但 var_3=1 将添加 score_sum+=0.12

通过首先按 df['usage'].unique() 定义的 models 循环解决,并且将有等量的 json 文件。然后我们遍历 json 中的每个相关变量,然后是给定变量的每个增量值。我们在每个循环中创建掩码,并从最低值开始,因为我们想累加这些值。

df['new_scores'] = 0
for model in models:
    for var in models[model]:
        var_name = list(var)[0]
        json_values = pd.DataFrame(var[var_name])
        json_values = json_values.set_index('lb')
        for value in json_values.index:
            mask = (df[var_name] >= float(value)) & (df['usage'] == model)
            df.loc[mask, 'new_scores'] += json_values.loc[value, 'b_cumul']

将旧解决方案中的列与新解决方案中的列进行比较时,值完全相同。对于约 600 万行的原始数据帧,它从花费约 4 小时迭代 5% 的 dataframe,到约 70 秒到 运行 遍历整个脚本。