Панды, как я могу избежать использования iterrow (как назначить значения новому столбцу в фрейме данных на основе значений из другого фрейма данных) - PullRequest
2 голосов
/ 23 октября 2019

У меня есть три разных объекта: Возможность , Аккаунт , Активность .

Мне нужно соединить их особым образом. Позвольте мне объяснить, как они связаны:

  • Возможность N-1 Счет
  • Счет 1-N Активность

Также стоит отметить, что

  • Возможность имеет следующее поле: { opp_id ; opp_date ; acc_id}
  • Действие имеет следующее поле: { act_id ; act_date ; acc_id }

Что я хотел бы достичь, так это вставить в Opportunity количество действий, которые были выполнены за X дней доДата возможности.

В настоящее время я делаю это так:

a_new_df = pd.DataFrame(columns=['acc_id',"opp_id", "opp_date", "act_90", "act_180"])

for index, opp_row in Opportunity.iterrows():
    account = opp_row["acc_id"]
    opportunity = opp_row["opp_id"]
    opp_date = opp_row["opp_date"]
    act_90, act_180 = 0, 0
    for index, act_row in activities_step_7.iterrows():
        if acc == act_row["acc_id"]:
            days = (pd.to_datetime(opp_date) - pd.to_datetime(act_row["act_date"])).days
            if days<=90:
                act_90+=1
            elif days<=180:
                act_180+=1
    events_df = events_df.append({
        "acc_id": account,
        "opp_id":  opportunity,
        "opp_date" : dat,
        "act_90" :  act_90,
        "act_180" : act_180,    
      }, ignore_index=True)

Наконец, я делаю merge() betwenn Возможность и этот новый дф. Операция, конечно, длится вечно. Тем не менее, я понятия не имею, как это улучшить.

Основная проблема заключается в том, что мне нужно вставить в Opportunity некоторую статистику, которая требует как данных из Opportunity, так и Activity, но я не могу объединить их заранее, потому что для каждой возможностиУ меня есть более одного вида деятельности (и вы не можете выполнить левое соединение, пока у вас есть дубликаты)

Есть идеи? Большое спасибо!


РЕДАКТИРОВАТЬ 1

Если это моя таблица возможностей:

    opp_date    acc_id  opp_id
0   05.08.2019  acc1    opp1
1   25.03.2019  acc2    opp2
2   27.08.2019  acc1    opp3
3   02.09.2019  acc1    opp4
4   22.07.2019  acc3    opp5

и это моя таблица активности:

    acc_id  act_date
0   acc1    25.07.2019
1   acc1    26.07.2019
2   acc1    31.07.2019 
3   acc1    28.07.2019
4   acc1    02.09.2019 
5   acc1    02.09.2019 
6   acc1    31.07.2019 
7   acc1    02.09.2019 
8   acc1    24.07.2019 
9   acc1    25.07.2019 
10  acc2    31.03.2019 
11  acc3    31.07.2019 
12  acc2    24.03.2019 
13  acc3    13.05.2019 
14  acc3    05.02.2019
15  acc3    30.05.2016 
16  acc3    30.11.2017 
17  acc3    11.04.2016 
18  acc3    19.01.2018 
19  acc3    19.01.2018 
20  acc2    24.03.2019 
21  acc1    04.08.2019
22  acc1    20.10.2019

тогда ожидаемый результат:

    opp_date        acc_id  opp_id      act_90  act_180
0   05.08.2019      acc1    opp1        4       4   
1   25.03.2019      acc2    opp2        0       0   
2   27.08.2019      acc1    opp3        7       8   
3   02.09.2019      acc1    opp4        0       0   
4   22.07.2019      acc3    opp5        2       2   

1 Ответ

1 голос
/ 23 октября 2019

Вы можете использовать некоторые встроенные функции pandas вместо цикла for. Этот результат немного отличается от «ожидаемого результата», который вы опубликовали в своем вопросе, но я думаю, что он соответствует вашему описанию.

Давайте назовем ваш первый фрейм данных df1, а второй - df2.
Мы можем посчитать, сколько действий соответствует вашему условию, записав его в виде функции и apply -изложив его вместо итерации по строкам:

def count_activities(row, act_df, days):
    return (act_df['act_date'].between(row['opp_date'] -pd.Timedelta(days=days), row['opp_date']) 
            & (act_df['acc_id']==row['acc_id'])).sum()

Поскольку мы выполняем подсчет в этой функциивыше, присоединение не является проблемой:

def add_count_activities_column(opp_df, act_df, days):
    return opp_df.join(opp_df.apply(lambda row: count_activities(row,act_df,days), axis=1).rename('act_{}'.format(days)))

И результат:

df3 = add_count_activities_column(df1, df2, 90)
df3 = add_count_activities_column(df3, df2, 180)

мой df3 равен

    opp_date    acc_id  opp_id  act_90  act_180
0   2019-05-08  acc1    opp1    4   4
1   2019-03-25  acc2    opp2    2   2
2   2019-08-27  acc1    opp3    7   8
3   2019-02-09  acc1    opp4    3   3
4   2019-07-22  acc3    opp5    2   2

пс - я бы использовалopp_id в качестве индекса, с df1.set_index('opp_id', inplace=True).

...