У меня есть немного сложная функция, которая назначает уровень качества заданным данным с помощью заранее определенных пошаговых логик c (зависит от фиксированных границ, а также от относительных границ на основе реального значения). Функция get_quality () ниже делает это для каждой строки, и использование pandas DataFrame.apply довольно медленно для огромных наборов данных. Поэтому я хотел бы векторизовать этот расчет. Очевидно, что я мог бы сделать что-то вроде df.groupby(pd.cut(df.ground_truth, [-np.inf, 10.0, 20.0, 50.0, np.inf]))
для внутреннего if-logi c, а затем применить аналогичную подгруппу внутри каждой группы (на основе границ каждой группы), но как бы я это сделал для последнего раздела, который зависит от заданного значения real / ground_truth в каждой строке?
Использование df['quality'] = np.vectorize(get_quality)(df['measured'], df['ground_truth'])
уже намного быстрее, но существует ли реальный векторизованный способ вычисления того же столбца 'quality'?
import pandas as pd
import numpy as np
from bisect import bisect
quality_levels = ['WayTooLow', 'TooLow', 'OK', 'TooHigh', 'WayTooHigh']
# Note: to make the vertical borders always lead towards the 'better' score we use a small epsilon around them
eps = 0.000001
def get_quality(measured_value, real_value):
diff = measured_value - real_value
if real_value <= 10.0:
i = bisect([-4.0-eps, -2.0-eps, 2.0+eps, 4.0+eps], diff)
return quality_levels[i]
elif real_value <= 20.0:
i = bisect([-14.0-eps, -6.0-eps, 6.0+eps, 14.0+eps], diff)
return quality_levels[i]
elif real_value <= 50.0:
i = bisect([-45.0-eps, -20.0-eps, 20.0+eps, 45.0+eps], diff)
return quality_levels[i]
else:
i = bisect([-0.5*real_value-eps, -0.25*real_value-eps,
0.25*real_value+eps, 0.5*real_value+eps], diff)
return quality_levels[i]
N = 100000
df = pd.DataFrame({'ground_truth': np.random.randint(0, 100, N),
'measured': np.random.randint(0, 100, N)})
df['quality'] = df.apply(lambda row: get_quality((row['measured']), (row['ground_truth'])), axis=1)
print(df.head())
print(df.quality2.value_counts())
# ground_truth measured quality
#0 51 1 WayTooLow
#1 7 25 WayTooHigh
#2 38 95 WayTooHigh
#3 76 32 WayTooLow
#4 0 18 WayTooHigh
#OK 30035
#WayTooHigh 24257
#WayTooLow 18998
#TooLow 14593
#TooHigh 12117