Более быстрый вложенный цикл for, возможно, с использованием pd.groupby - PullRequest
0 голосов
/ 03 июля 2019

У меня есть вложенный цикл.Но мой набор данных очень большой, поэтому мне нужен более быстрый способ.Я полагаю, что это можно сделать с помощью группировки или отображения данных каким-то умным способом.У меня есть список имен X.Для каждого имени у меня есть предметы, которые состоят из цвета, бренда и стоимости.Мне нужно суммировать значения для каждой комбинации цвета и бренда для каждого имени.Затем выполните некоторые операции с этими суммами, сначала внутри каждого цвета, затем с каждым именем, отслеживая комбинацию, по которой вычисляется сумма.Вложенный цикл for становится очень медленным, так как мой набор данных большой.Есть ли более быстрый способ?

import random
import pandas
list1 = ['Name 0']
list2 = ['Color 0']
list3 = ['Brand 0']
list4 = [random.randint(10,1000)]
nName = 25
nColor = 5
nBrand = 3
nSim = 1000
for i in range(1,nSim):
    list1.extend(['Name {}'.format(random.randint(0,nName))])
    list2.extend(['Color {}'.format(random.randint(0,nColor))])
    list3.extend(['Brand {}'.format(random.randint(0, nBrand))])
    list4.append(random.randint(10,1000))
d = list(zip(list1, list2, list3, list4))
df1 = pd.DataFrame(d, columns=['Name','Color','Brand','Value'])

end_values = np.zeros(nName)
for iName in range(0,nName):
    y = 0
    for iColor in range(0,nColor):
        x = np.zeros(nBrand)
        for iBrand in range(0,nBrand):
            x[iBrand] = np.nansum(df1[((df1['Name'] == 'Name {}'.format(iName)) & (df1['Color'] == 'Color {}'.format(iColor)) & (df1['Brand'] == 'Brand {}'.format(iBrand)))]['Value'])
        y = y + x[0] + 1.5 * x[1] + 3 * x[0] * x[2]
    end_values[iName] = y

1 Ответ

0 голосов
/ 03 июля 2019

Не ясно, в чем ваша проблема на самом деле. Потому что в коде, который вы указали в вопросе, нет вложенных циклов for. Вы имеете в виду вложенные циклы для всех комбинаций значений Name, Color и Brand? Если это так, вам не нужно писать какие-либо циклы. Просто используйте pandas означает:

df2 = df1.groupby(['Name', 'Color', 'Brand']).agg({'Value': 'sum'})

Да, одна строка для их вычисления. Как group by в SQL. Например, num22 - это df2.loc[('Name 0', 'Color 2', 'Brand 2'), 'Value'].

В случае немного более сложных расчетов

df3 = df1.groupby(['Name', 'Color', 'Brand']).agg({'Value': ['count', 'sum']})

num22 - это df3.loc[('Name 0', 'Color 2', 'Brand 2'), ('Value', 'sum')].

Аргумент

.agg(...) - это словарь, в котором вы указываете, какие столбцы вы хотите обработать и какую статистику вы хотите получить.

Попробуйте так и скажите, не понимаю ли я вашу проблему.

EDIT

Рассмотрим следующий код:

# 1. original code with nested loops
# includes slight correction (+1 in range(...) and zeros(...) functions)
def f_1(df1):
    end_values = np.zeros(nName + 1)
    for iName in range(nName + 1):
        y = 0
        for iColor in range(nColor + 1):
            x = np.zeros(nBrand + 1)
            for iBrand in range(nBrand + 1):
                x[iBrand] = np.nansum(df1[((df1['Name'] == 'Name {}'.format(iName)) & (df1['Color'] == 'Color {}'.format(iColor)) & (df1['Brand'] == 'Brand {}'.format(iBrand)))]['Value'])
            y = y + x[0] + 1.5 * x[1] + 3 * x[0] * x[2]
        end_values[iName] = y
    return end_values

# 3. new code - supportive function to calculate result for brands
def f_2_suppport(srs):
    cnt = {k: v for k, v in zip(srs.index.get_level_values(2), srs)}
    a = cnt.get('Brand 0', 0)
    b = cnt.get('Brand 1', 0)
    c = cnt.get('Brand 2', 0)
    return a + 1.5 * b + 3 * a * c

# 2. new code - main function with pandas groupby/agg
def f_2(df1):

    return (
        df1
        .groupby(['Name', 'Color', 'Brand'])
        .agg({'Value': 'sum'})
        .groupby(['Name', 'Color'])
        .agg({'Value': lambda x: f_2_suppport(x)})
        .groupby('Name')
        .agg({'Value': 'sum'}))


if __name__ == '__main__':

    print(f_1(df1))
    print(f_2(df1))

Выход:

[28735057.  26111382.  28304153.5 15495841.  15915862.5 13884354.5
 21172873.  27342470.5 29403143.  38212958.5 37504605. ]
              Value
Name               
Name 0   28735057.0
Name 1   26111382.0
Name 10  37504605.0
Name 2   28304153.5
Name 3   15495841.0
Name 4   15915862.5
Name 5   13884354.5
Name 6   21172873.0
Name 7   27342470.5
Name 8   29403143.0
Name 9   38212958.5

Время выполнения на моей машине:

%timeit f_1(df1)
1.07 s ± 736 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit f_2(df1)
35.2 ms ± 163 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

Итак groupby + agg сделать расчет примерно в 30 раз быстрее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...