Решение Pythoni c для повышения эффективности во время выполнения - PullRequest
2 голосов
/ 31 марта 2020

Я хотел бы улучшить время выполнения программы python, которая принимает фрейм данных pandas и создать две новые переменные (дата группы и группы) на основе нескольких условий (код и лог c приведены ниже). Код отлично работает на небольших наборах данных, но на больших наборах данных (20 миллионов строк) для его выполнения требуется более 7 часов.

Logi c за кодом

  1. , если идентификатор является первым идентифицированным идентификатором, тогда group = 1 и groupdate = date
  2. , если не первый идентификатор и дата - предыдущая дата> 10 или дата - предыдущая дата группы> 10, затем группа = предыдущая группа # + 1 и дата группы = дата
  3. , если не первый идентификатор и дата - предыдущая дата <= 10 или дата - предыдущая дата группы <= 10 затем group = предыдущая group # и groupdate = предыдущая groupdate. </li>

Пример кода

import pandas as pd
import numpy as np

ID = ['a1','a1','a1','a1','a1','a2','a2','a2','a2','a2']
DATE = ['1/1/2014','1/15/2014','1/20/2014','1/22/2014','3/10/2015', \
        '1/13/2015','1/20/2015','1/28/2015','2/28/2015','3/20/2015']
ITEM = ['P1','P2','P3','P4','P5','P1','P2','P3','P4','P5']

df = pd.DataFrame({"ID": ID, "DATE": DATE, "ITEM": ITEM})
df['DATE']= pd.to_datetime(df['DATE'], format = '%m/%d/%Y')

ids=df.ID
df['first_id'] = np.where((ids!=ids.shift(1)), 1, 0) 
df['last_id'] = np.where((ids!=ids.shift(-1)), 1, 0) 

print(df); print('\n')

for i in range(0,len(df)):
    if df.loc[i,'first_id']==1:
        df.loc[i,'group'] = 1
        df.loc[i,'groupdate'] = df.loc[i,'DATE']

    elif df.loc[i,'first_id']==0 and ((df.loc[i,'DATE'] - df.loc[i-1,'DATE']).days > 10) or \
                                      ((df.loc[i,'DATE'] - df.loc[i-1,'groupdate']).days > 10):
                                            df.loc[i,'group'] = df.loc[i-1,'group'] + 1
                                            df.loc[i,'groupdate'] = df.loc[i,'DATE']     

    else: 
        if df.loc[i,'first_id']==0 and ((df.loc[i,'DATE'] - df.loc[i-1,'DATE']).days <= 10) or \
                                    ((df.loc[i,'DATE'] - df.loc[i-1,'groupdate']).days <= 10):
                                        df.loc[i,'group'] = df.loc[i-1,'group']
                                        df.loc[i,'groupdate'] = df.loc[i-1,'groupdate']

print(df); print('\n')    

Выход

ID  DATE        ITEM    GROUP   GROUPDATE
1   1/1/2014    P1      1       1/1/2014
1   1/15/2014   P2      2       1/15/2014
1   1/20/2014   P3      2       1/15/2014
1   1/22/2014   P4      2       1/15/2014
1   3/10/2015   P5      3       3/10/2015
2   1/13/2015   P1      1       1/13/2015
2   1/20/2015   P2      1       1/13/2015
2   1/28/2015   P3      2       1/28/2015
2   2/28/2015   P4      3       2/28/2015
2   3/20/2015   P5      4       3/20/2015

1 Ответ

0 голосов
/ 02 апреля 2020

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

  • Я думаю, что ваш код создает некоторые проблемы при переходе из группы в другую.
  • Вам следует избегать группы, поэтому я использую groupby
  • I Я не использую здесь ваши логи c о previous_groupdate

Генерация данных

import pandas as pd
import numpy as np

ID = ['a1','a1','a1','a1','a1','a2','a2','a2','a2','a2']
DATE = ['1/1/2014','1/15/2014','1/20/2014','1/22/2014','3/10/2015', \
        '1/13/2015','1/20/2015','1/28/2015','2/28/2015','3/20/2015']
ITEM = ['P1','P2','P3','P4','P5','P1','P2','P3','P4','P5']

df = pd.DataFrame({"ID": ID, "DATE": DATE, "ITEM": ITEM})
df['DATE']= pd.to_datetime(df['DATE'], format = '%m/%d/%Y')

ids=df.ID
df['first_id'] = np.where((ids!=ids.shift(1)), 1, 0) 

Функция, которая работает для каждого "ID"

def fun(x):
    # To compare with previous date I add a column
    x["PREVIOUS_DATE"] = x["DATE"].shift(1)
    x["DATE_DIFF1"] = (x["DATE"]-x["PREVIOUS_DATE"]).dt.days
    # These are your simplified conditions
    conds = [x["first_id"]==1,
             ((x["first_id"]==0) & (x["DATE_DIFF1"]>10)),
             ((x["first_id"]==0) & (x["DATE_DIFF1"]<=10))]
    # choices for date
    choices_date = [x["DATE"].astype(str),
                    x["DATE"].astype(str),
                    '']
    # choices for group
    # To get the expected output we'll need a cumsum
    choices_group = [ 1, 1, 0]
    # I use np.select you can check how it works
    x["group_date"] = np.select(conds, choices_date, default="")
    x["group"] = np.select(conds, choices_group, default=0)
    # some group_date are empty so I fill them
    x["group_date"] = x["group_date"].astype("M8[us]").fillna(method="ffill")
    # Here is the cumsum
    x["group"] = x["group"].cumsum()
    # Remove columns we don't need
    x = x.drop(["first_id", "PREVIOUS_DATE", "DATE_DIFF1"], axis=1)
    return x

Как использовать

df = df.groupby("ID").apply(fun)
   ID       DATE ITEM group_date  group
0  a1 2014-01-01   P1 2014-01-01      1
1  a1 2014-01-15   P2 2014-01-15      2
2  a1 2014-01-20   P3 2014-01-15      2
3  a1 2014-01-22   P4 2014-01-15      2
4  a1 2015-03-10   P5 2015-03-10      3
5  a2 2014-01-01   P1 2014-01-01      1
6  a2 2014-01-15   P2 2014-01-15      2
7  a2 2014-01-20   P3 2014-01-15      2
8  a2 2014-01-22   P4 2014-01-15      2
9  a2 2015-03-10   P5 2015-03-10      3

Ускорение

Здесь вы можете подумать об использовании dask , modin или cuDF см. modin vs cuDF Но, вероятно, вам следует поработать над тем, как организовать ваши данные перед их обработкой. Я говорю о чем-то вроде это это мое, извините, но дает вам представление о том, как правильно разделить данные могут ускорить процесс.

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