Как оптимизировать несколько операций над кадрами данных Python, которые в настоящее время выполняются со столбцами и итерациями строк? - PullRequest
0 голосов
/ 20 октября 2019

В настоящее время у меня есть процесс, который работает с несколькими итерациями по строкам и столбцам. Я считаю, что должен быть более эффективный способ вычисления выходных данных с использованием неких векторизованных функций на фрейме данных в сочетании с group by;тем не менее, я не знаю, как это сделать.

Этот код довольно быстро работает с образцом набора данных, но я выполняю их несколько раз по множеству записей.

Это пример кода:

import pandas as pd
import numpy as np
from scipy import stats

data_metric1 = list(np.random.rand(100)) + list(np.random.rand(100) + np.random.randint(-1, 1, 100) * 0.1)
data_metric2 = list(np.random.rand(100)) + list(np.random.rand(100) + np.random.randint(-1, 1, 100) * 0.1)

test_label = list(np.random.choice(['test', 'control'], 100))
dimensions = list(np.random.choice(['a', 'b'], 100))

df = pd.DataFrame(list(zip(test_label, dimensions, data_metric1, data_metric2)), columns=['tc', 'dim', 'metric1', 'metric2'])

cols = ['dim', 'col', 'control_count', 'test_count', 'mean_control', 'mean_test', 'lift', 'p_value_utest']
out_list = []

def run_test_detail(df, dim, col):
    control_data = df[df.tc == 'control'][col]
    test_data = df[df.tc == 'test'][col]
    mean_control = control_data.mean()
    mean_test = test_data.mean()
    control_count = control_data.size
    test_count = test_data.size

    lift = (mean_test - mean_control) / mean_control * 100
    p_value_utest = stats.mannwhitneyu(control_data, test_data)[1]

    r = [dim, col, control_count, test_count, mean_control, mean_test, lift, p_value_utest]
    out_list.append(r)


def run_test(df):
    dims = df['dim'].unique()
    cols = [col for col in df.columns if col not in ['tc', 'dim']]
    for col in cols:
        run_test_detail(df, 'all', col)

    for dim in dims:
        df_dim = df[df['dim'] == dim]
        for col in cols:
            run_test_detail(df_dim, dim, col)

run_test(df)
out_df = pd.DataFrame(out_list, columns=cols)
print(tabulate(out_df, headers='keys', showindex=False))
dim    col        control_count    test_count    mean_control    mean_test       lift    p_value_utest
-----  -------  ---------------  ------------  --------------  -----------  ---------  ---------------
all    metric1               56            44        0.520419     0.499656   -3.98968        0.285719
all    metric2               56            44        0.460029     0.540992   17.5995         0.0798384
b      metric1               28            22        0.53479      0.546289    2.15024        0.488306
b      metric2               28            22        0.496291     0.596768   20.2456         0.114689
a      metric1               28            22        0.506048     0.453023  -10.4783         0.22006
a      metric2               28            22        0.423766     0.485215   14.5006         0.157083

1 Ответ

0 голосов
/ 20 октября 2019

Я реорганизовал ваше решение с помощью мультипроцессинга. Если у вас много метрик и измерений, то оно должно хорошо масштабироваться. run_test_details можно оптимизировать с помощью некоторого кэширования, но я не знаю, насколько велики ваши данные, поэтому это может быть проблематично. Также, если бы это было стандартным статистическим процессом, я бы проверил R.

Пожалуйста, дайте мне знать, если это улучшит ваше время выполнения.

import pandas as pd
import numpy as np
from scipy import stats
import multiprocessing as mp


data_metric1 = list(np.random.rand(100)) + list(np.random.rand(100) + np.random.randint(-1, 1, 100) * 0.1)
data_metric2 = list(np.random.rand(100)) + list(np.random.rand(100) + np.random.randint(-1, 1, 100) * 0.1)

test_label = list(np.random.choice(['test', 'control'], 100))
dimensions = list(np.random.choice(['a', 'b'], 100))

df = pd.DataFrame(list(zip(test_label, dimensions, data_metric1, data_metric2)), columns=['tc', 'dim', 'metric1', 'metric2'])

##############

def run_test_detail(df, dim, col):
    control_data = df[df.tc == 'control'][col]
    test_data = df[df.tc == 'test'][col]
    mean_control = control_data.mean()
    mean_test = test_data.mean()
    control_count = control_data.size
    test_count = test_data.size

    lift = (mean_test - mean_control) / mean_control * 100
    p_value_utest = stats.mannwhitneyu(control_data, test_data)[1]

    r = [dim, col, control_count, test_count, mean_control, mean_test, lift, p_value_utest]
    return r


##############

pool = mp.Pool(processes=mp.cpu_count())

result1 = pool.starmap(run_test_detail, [(df, 'all', col) 
                       for col in df.columns.difference(['tc', 'dim'])])

result2 = pool.starmap(run_test_detail, [(df[df['dim'] == dim], dim, col) 
                       for col in df.columns.difference(['tc', 'dim']) 
                       for dim in df['dim'].unique()])

##############

cols = ['dim', 'col', 'control_count', 'test_count', 'mean_control', 'mean_test', 'lift', 'p_value_utest']
out_df = pd.DataFrame(data=result1+result2, columns=cols)
...