Динамическое индексирование с определенной длиной суммы в пандах - PullRequest
0 голосов
/ 01 июня 2018

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

    V1
1   100000
2   50000
3   20000
4   30000
5   150000
6   30000
7   20000
8   200000

И мне нужно получить индексы, где каждый раз, когда сумма V1 достигает 50k точно.Поэтому, когда значение V1 превышает ограничение в 50 КБ, индекс повторяется столько раз, сколько будет достигнуто ограничение, либо, если значение V1 ниже, чем ограничение в 50 КБ, группирует строки до тех пор, пока предел 50 К не будет достигнут или не пройден.Результат будет примерно таким:

    V1
1   100000
1   100000
2   50000
4   30000
5   150000
5   150000
5   150000
7   20000
8   200000
8   200000
8   200000
8   200000

Мне удалось разрешить это с помощью циклов, но мне интересно, возможно ли это с помощью группирования с функциями pandas.

1 Ответ

0 голосов
/ 02 июня 2018

В примере, который вы даете с помощью

df = pd.DataFrame({'V1':[100000,50000, 20000, 30000, 
                         150000, 30000, 20000, 200000]},
                   index=range(1,9))

Из того, что я понимаю, " получите индексы, где каждый раз, когда сумма V1 достигает 50k точно ", вы можетеСделайте это, создав столбец с числом ровно 50 тыс., которое у вас есть в сумме до каждой строки:

ser_50k = (df.V1.cumsum()/50000).astype(int)
df['nb_50'] = (ser_50k - ser_50k.shift()).fillna(ser_50k).astype(int)

Теперь вы можете использовать stack для создания строк с одинаковым индексом, когда 50k достигает нескольких раз:

df_join = (df['nb_50'].apply(lambda x: pd.Series(range(x)))
                      .stack().reset_index(level=1).drop('level_1',1))
df = df.join(df_join).dropna().drop(['nb_50',0],1)

И вы получите ожидаемый результат с вашим примером ввода.

Проблема в том, что когда у вас есть

df= pd.DataFrame({'V1':[180000, 20000, 30000, 50000]}) 

Мой метод дает вам:

       V1
0  180000
0  180000
0  180000
1   20000
3   50000

, и некоторые ошибки могут произойти позже, в то время как когда вы говорите «, когда значение V1 превышает предел 50k, индекс повторяется столько раз, сколько достигается предел, или если значение V1 равнониже предела 50k группирует строки до тех пор, пока предел 50k не будет достигнут или не пройден", и я понимаю, что вы ожидаете:

       V1
0  180000
0  180000
0  180000
2   30000
3   50000

В этом случае, если вы не хотите использоватьцикл, когда число превышает 50K, вы можете сделать (примерно то же самоеИдея, чем прежде):

df = df.join(df['V1'].apply(lambda x: pd.Series(range(x/50000)))
                       .stack().reset_index(level=1).drop('level_1',1)).drop(0,1)

Но я не мог найти простой способ сделать случай, когда число меньше 50 КБ, поэтому ваш цикл for может быть достаточно.в противном случае, я подумал об этом:

def nb_group_under(v1):
    global nb_group
    if v1 < 50000:
        return nb_group
    else:
        nb_group += 1
nb_group = 1
df['under_50'] = df['V1'].apply(nb_group_under)

затем вы пытаетесь найти, где 50k передается в созданной группе:

df['sum_under50'] = (df.groupby('under_50').V1.cumsum()/50000).astype(int)
df['sum_under50'] = df.sum_under50 - (df.groupby('under_50').sum_under50
                                        .shift().fillna(df.sum_under50))
df = (df[(df['sum_under50']>0) | (df['V1'] >= 50000)]
              .drop(['under_50', 'sum_under50'],1))

Я не особенно доволен методом длядело до 50, но не мог думать иначе.

Надеюсь, что в любом случае это будет полезно или даст вам некоторые идеи о том, как решить вашу проблему без цикла

РЕДАКТИРОВАТЬ: для более общего решения вы можете создать функцию, возвращающую число раз, которое 50kв значении v1 или сделать частичную сумму, возвращающую 1, когда выше 50k, вам все еще нужна глобальная переменная:

def nb_lim_reached (v1, lim_v1):
    global partial_sum
    if v1 >= lim_v1:
        partial_sum = 0
        return pd.np.floor(v1/lim_v1)
    else:
        partial_sum += v1
        if partial_sum >= lim_v1:
            partial_sum -= lim_v1
            return 1
        else:
            return 0

Теперь вы можете создать еще один столбец с этой функцией:

v1_lim = 50000
partial_sum = 0
df['nb_lim'] = df['V1'].apply(nb_lim_reached, args=( v1_lim,)).astype(int)

Теперь вы используете ту же идею, что и мое решение reviosu с pd.Series и stack:

df = (df.join(df['nb_lim'].apply(lambda nb: pd.Series(range(nb)))
          .stack().reset_index(level=1).drop('level_1',1))
            .dropna().drop(['nb_lim',0],1))
...