Создать столбец на основе ограничения в пандах - PullRequest
5 голосов
/ 02 ноября 2019
F_Date      B_Date      col   is_B
01/09/2019  02/08/2019  2200    1
01/09/2019  03/08/2019  672     1
02/09/2019  03/08/2019  1828    1
01/09/2019  04/08/2019  503     0
02/09/2019  04/08/2019  829     1
03/09/2019  04/08/2019  1367    0
02/09/2019  05/08/2019  559     1
03/09/2019  05/08/2019  922     1
04/09/2019  05/08/2019  1519    0
01/09/2019  06/08/2019  376     1

Я хочу сгенерировать столбец c_a таким образом, чтобы для первого ввода flight_date изначально значение было 25000 и уменьшалось в зависимости от значения col. Например:

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

F_Date      B_Date      col   is_B   c_a
01/09/2019  02/08/2019  2200    1    25000
01/09/2019  03/08/2019  672     1    25000 - 2200
02/09/2019  03/08/2019  1828    1    25000
01/09/2019  04/08/2019  503     0    25000 - 2200 - 672
02/09/2019  04/08/2019  829     1    25000 - 1828
03/09/2019  04/08/2019  1367    0    25000
02/09/2019  05/08/2019  559     1    25000 - 1828 - 829
03/09/2019  05/08/2019  922     1    25000 (since last value had is_B as 0)
04/09/2019  05/08/2019  1519    0    25000
01/09/2019  06/08/2019  376     1    25000 - 2200 - 672 (Since last appearance had is_B as 0)

Может ли кто-нибудь определить способ панды для достижения того же?

Ответы [ 4 ]

3 голосов
/ 02 ноября 2019

Я думаю, я нашел довольно краткое решение:

df['c_a'] = df.groupby('F_Date').apply(lambda grp:
    25000 - grp.col.where(grp.is_B.eq(1), 0).shift(fill_value=0)
    .cumsum()).reset_index(level=0, drop=True)

Результат:

       F_Date      B_Date   col  is_B    c_a
0  01/09/2019  02/08/2019  2200     1  25000
1  01/09/2019  03/08/2019   672     1  22800
2  02/09/2019  03/08/2019  1828     1  25000
3  01/09/2019  04/08/2019   503     0  22128
4  02/09/2019  04/08/2019   829     1  23172
5  03/09/2019  04/08/2019  1367     0  25000
6  02/09/2019  05/08/2019   559     1  22343
7  03/09/2019  05/08/2019   922     1  25000
8  04/09/2019  05/08/2019  1519     0  25000
9  01/09/2019  06/08/2019   376     1  22128

Идея с примерами, основанными на группе F_Date =='01 / 09/2019 ':

  1. grp.col.where(grp.is_B.eq(1), 0) - значение, которое нужно вычесть из следующей строки в группе:

    0    2200
    1     672
    3       0
    9     376
    
  2. .shift(fill_value=0) - значение, которое нужно вычесть из строки current в группе:

    0       0
    1    2200
    3     672
    9       0
    
  3. .cumsum() - накопленозначения для вычитания:

    0       0
    1    2200
    3    2872
    9    2872
    
  4. 25000 - ... - целевое значение:

    0    25000
    1    22800
    3    22128
    9    22128
    
1 голос
/ 02 ноября 2019

Хорошая игра в панд: :)

import pandas as pd
df = pd.DataFrame({'F_Date': [pd.to_datetime(_, format='%d/%m/%Y') for _ in
                              ['01/09/2019', '01/09/2019', '02/09/2019', '01/09/2019', '02/09/2019',
                               '03/09/2019', '02/09/2019', '03/09/2019', '04/09/2019', '01/09/2019']],
                   'B_Date': [pd.to_datetime(_, format='%d/%m/%Y') for _ in
                              ['02/08/2019', '03/08/2019', '03/08/2019', '04/08/2019', '04/08/2019',
                               '04/08/2019', '05/08/2019', '05/08/2019','05/08/2019', '06/08/2019']],
                   'col': [2200, 672, 1828, 503, 829, 1367, 559, 922, 1519, 376],
                   'is_B': [1, 1, 1, 0, 1, 0, 1, 1, 0, 1]
                   })

Давайте пройдем по шагам:

# sort in the order that fits the semantics of your calculations
df.sort_values(['F_Date', 'B_Date'], inplace=True)

# initialize 'c_a' to 25000 if a new F_Date starts
df.loc[df['F_Date'].diff(1) != pd.Timedelta(0), 'c_a'] = 25000

# Step downwards from every 25000 and substract shifted 'col'
# if shifted 'is_B' == 1, otherwise replicate shifted 'c_a' to the next line
while pd.isna(df.c_a).any():
    df.c_a.where(
        pd.notna(df.c_a),   # set every not-NaN value to ...
        df.c_a.shift(1).where(       # ...the previous / shifted c_a...
            df.is_B.shift(1) == 0,   # ... if previous / shifted is_B == 0
            df.c_a.shift(1) - df.col.shift(1)   # ... otherwise substract shifted 'col'
        ), inplace=True
    )

# restore original order
df.sort_index(inplace=True)

Вот результат, который я получаю

      F_Date     B_Date   col  is_B      c_a
0 2019-09-01 2019-08-02  2200     1  25000.0
1 2019-09-01 2019-08-03   672     1  22800.0
2 2019-09-02 2019-08-03  1828     1  25000.0
3 2019-09-01 2019-08-04   503     0  22128.0
4 2019-09-02 2019-08-04   829     1  23172.0
5 2019-09-03 2019-08-04  1367     0  25000.0
6 2019-09-02 2019-08-05   559     1  22343.0
7 2019-09-03 2019-08-05   922     1  25000.0
8 2019-09-04 2019-08-05  1519     0  25000.0
9 2019-09-01 2019-08-06   376     1  22128.0
0 голосов
/ 02 ноября 2019

Ответ на ваш вопрос состоит из двух частей, первое, что вам нужно сделать, это сгруппировать фрейм данных по F_Date. Если у вас есть это, вы можете использовать rolling(), чтобы выполнить операцию над всеми предыдущими значениями данного значения. Здесь есть некоторые проблемы:

  1. Прокрутка может выполняться только в одном столбце за раз
  2. Вы можете вернуть только одно действительное значение при использовании expanding.apply

Мы можем обойти это, передавая как групповой фрейм данных, так и начальный фрейм данных методу, который мы используем для применения и установки значений там, это решение может быть не идеальным или не лучшим, если оно будет использованопросто работает.

In [1]: s = '''F_Date      B_Date      col   is_B
   ...: 01/09/2019  02/08/2019  2200    1
   ...: 01/09/2019  03/08/2019  672     1
   ...: 02/09/2019  03/08/2019  1828    1
   ...: 01/09/2019  04/08/2019  503     0
   ...: 02/09/2019  04/08/2019  829     1
   ...: 03/09/2019  04/08/2019  1367    0
   ...: 02/09/2019  05/08/2019  559     1
   ...: 03/09/2019  05/08/2019  922     1
   ...: 04/09/2019  05/08/2019  1519    0
   ...: 01/09/2019  06/08/2019  376     1'''

In [2]: import re

In [3]: sl = [re.split('\s+',x) for x in s.split('\n')]

In [4]: import pandas as pd

In [5]: df = pd.DataFrame(sl[1:], columns=sl[0])

In [6]: df['F_Date'] = df['F_Date'].astype('datetime64[ns]')

In [7]: df['B_Date'] = df['B_Date'].astype('datetime64[ns]')

In [8]: df['col'] = df['col'].astype(int)

In [9]: df['is_B'] = df['is_B'].astype(int)

In [10]: df['c_a'] = None

In [11]: def l(df, df_g, cols):
    ...:      is_Bs = df_g['is_B'].values[:len(cols)]
    ...:      values = [2500]+ [cols[i] for i in range(len(cols)-1) if is_Bs[i] ]
    ...:      df.at[df_g.index[len(cols)-1], 'c_a'] = values
    ...:      return 1

In [12]: for dt, df_g in df.groupby('F_Date'):
    ...:     df_g['col'].expanding().apply(lambda x: l(df, df_g, x),raw= True)
    ...: 
In [13]: df

Out[13]: 
      F_Date     B_Date   col  is_B                    c_a
0 2019-01-09 2019-02-08  2200     1                 [2500]
1 2019-01-09 2019-03-08   672     1         [2500, 2200.0]
2 2019-02-09 2019-03-08  1828     1                 [2500]
3 2019-01-09 2019-04-08   503     0  [2500, 2200.0, 672.0]
4 2019-02-09 2019-04-08   829     1         [2500, 1828.0]
5 2019-03-09 2019-04-08  1367     0                 [2500]
6 2019-02-09 2019-05-08   559     1  [2500, 1828.0, 829.0]
7 2019-03-09 2019-05-08   922     1                 [2500]
8 2019-04-09 2019-05-08  1519     0                 [2500]
9 2019-01-09 2019-06-08   376     1  [2500, 2200.0, 672.0]
0 голосов
/ 02 ноября 2019

Попробуйте сгруппировать с shift, cumsum и ffill

m = ~df.groupby('F_Date').is_B.diff().eq(1)
s = (-df.col).groupby(df.F_Date).apply(lambda x: x.shift(fill_value=25000).cumsum())

df['c_a'] = s.where(m).groupby(df.F_Date).ffill()


Out[98]:
       F_Date      B_Date   col  is_B      c_a
0  01/09/2019  02/08/2019  2200     1  25000.0
1  01/09/2019  03/08/2019   672     1  22800.0
2  02/09/2019  03/08/2019  1828     1  25000.0
3  01/09/2019  04/08/2019   503     0  22128.0
4  02/09/2019  04/08/2019   829     1  23172.0
5  03/09/2019  04/08/2019  1367     0  25000.0
6  02/09/2019  05/08/2019   559     1  22343.0
7  03/09/2019  05/08/2019   922     1  25000.0
8  04/09/2019  05/08/2019  1519     0  25000.0
9  01/09/2019  06/08/2019   376     1  22128.0
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...