Более эффективный способ вычисления/добавления новых столбцов с использованием Панд для большого набора данных

#python #pandas #dataframe #bigdata

Вопрос:

У меня есть следующий набор данных:

               CustomerID Date                     Amount           Department  
0                 395134 2019-01-01               199              Home   
1                 395134 2019-01-01               279              Home   
2                1356012 2019-01-07               279              Home   
3                1921374 2019-01-08               269              Home   
4                 395134 2019-01-01               279              Home   
...                  ...        ...               ...               ...   
18926474         1667426 2021-06-30               349        Womenswear   
18926475         1667426 2021-06-30               299        Womenswear   
18926476          583105 2021-06-30               349        Womenswear   
18926477          538137 2021-06-30               279        Womenswear   
18926478          825382 2021-06-30              2499              Home   

                  DaysSincePurchase  
0                 986 days  
1                 986 days  
2                 980 days  
3                 979 days  
4                 986 days  
...                    ...  
18926474           75 days  
18926475           75 days  
18926476           75 days  
18926477           75 days  
18926478           75 days  
 

Я хочу заняться разработкой некоторых функций и добавить несколько столбцов после агрегирования (с помощью group_by) по идентификатору клиента. Столбец Даты не имеет значения и может быть легко удален. Мне нужен набор данных, в котором каждая строка представляет собой один уникальный идентификатор пользователя, который является просто целыми числами 1,2… (первый столбец), где остальные столбцы:

  1. Общая сумма покупки
  2. Дней с момента последней покупки
  3. Общее количество отделов

Это то, что я сделал, и это работает. Однако, когда я засекаю время, это занимает около 1,5 часов. Есть ли другой более эффективный способ сделать это?

 customer_group = joinedData.groupby(['CustomerID'])
n = originalData['CustomerID'].nunique()

# First arrange the data in a matrix.
matrix = np.zeros((n,5)) # Pre-allocate matrix

for i in range(0,n):
    matrix[i,0] = i 1
    matrix[i,1] = sum(customer_group.get_group(i 1)['Amount'])
    matrix[i,2] = min(customer_group.get_group(i 1)['DaysSincePurchase']).days
    matrix[i,3] = customer_group.get_group(i 1)['Department'].nunique()

# The above loop takes 6300 sec approx

# convert matrix to dataframe and name columns
newData = pd.DataFrame(matrix)
newData = newData.rename(columns = {0:"CustomerID"})
newData = newData.rename(columns = {1:"TotalDemand"})
newData = newData.rename(columns = {2:"DaysSinceLastPurchase"})
newData = newData.rename(columns = {3:"nrDepartments"})
 

Комментарии:

1. Не могли бы вы поделиться набором данных?

2. @Люк, я поместил его фотографию в пост, разве тебе это не видно? Или вы имеете в виду загрузку всего набора данных? К сожалению, я не могу его загрузить.

3. Если бы вы могли предоставить весь набор данных, сообщество SO смогло бы разработать подход и сравнить его производительность с вашей, иначе этот вопрос был бы довольно бессмысленным

4. @Luke — я полностью понимаю и хотел бы понять. Но такой обмен файлами данных противоречит политике компании :/

Ответ №1:

Воспользуйся agg :

 >>> df.groupby('CustomerID').agg(TotalDemand=('Amount', sum), 
                                 DaysSinceLastPurchase=('DaysSincePurchase', min),
                                 nrDepartments=('Department', 'nunique'))
 

Я запустил эту функцию над фреймом данных из 20 000 000 записей. Это заняло несколько секунд, чтобы быть выполненным:

 >>> %timeit df.groupby('CustomerID').agg(...)
14.7 s ± 225 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
 

Сгенерированные данные:

 N = 20000000
df = pd.DataFrame(
    {'CustomerID': np.random.randint(1000, 10000, N), 
     'Date': np.random.choice(pd.date_range('2020-01-01', '2020-12-31'), N),
     'Amount': np.random.randint(100, 1000, N),
     'Department': np.random.choice(['Home', 'Sport', 'Food', 'Womenswear',
                                     'Menswear', 'Furniture'], N)})
df['DaysSincePurchase'] = pd.Timestamp.today().normalize() - df['Date']