панды-связывание с определениями ячеек на основе значения в другом столбце


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

Для минимального рабочего примера давайте определим простой фрейм данных:

import pandas as pd
df = pd.DataFrame({'A' : ['one', 'one', 'two', 'three'] * 3,'B' : np.random.randn(12)})

Фрейм данных выглядит следующим образом:

        A       B
0       one     2.5772143847077427
1       one     -0.6394141654096013
2       two     0.964652049995486
3       three   -0.3922889559403503
4       one     1.6903991754896424
5       one     0.5741442025742018
6       two     0.6300564981683544
7       three   0.9403680915507433
8       one     0.7044433078166983
9       one     -0.1695006646595688
10      two     0.06376190217285167
11      three   0.277540580579127

Теперь я хотел бы ввести столбец C, который будет содержать метку bin, с различными ячейками для каждого из значений в столбце A, т. е.:

  • (-10,-1,0,1,10) для A == 'one',
  • (-100,0,100) для A == 'two',
  • (-999,0,1,2,3) для A == 'three'.

Желаемый результат:

        A       B       C
0       one     2.5772143847077427      (1, 10]
1       one     -0.6394141654096013     (-1, 0]
2       two     0.964652049995486       (0, 100]
3       three   -0.3922889559403503     (-999, 0]
4       one     1.6903991754896424      (1, 10]
5       one     0.5741442025742018      (0, 1]
6       two     0.6300564981683544      (0, 100]
7       three   0.9403680915507433      (0, 1]
8       one     0.7044433078166983      (0, 1]
9       one     -0.1695006646595688     (-1, 0]
10      two     0.06376190217285167     (0, 100]
11      three   0.277540580579127       (0, 1]

Я пробовал использовать pd.cut или np.digitize с различными комбинациями map, apply, но безуспешно.

В настоящее время я добиваюсь результата, разбивая кадр и применяя pd.cut к каждому подмножеству отдельно, а затем объединяя, чтобы получить кадр обратно, как это:

values_in_column_A = df['A'].unique().tolist()
bins = {'one':(-10,-1,0,1,10),'two':(-100,0,100),'three':(-999,0,1,2,3)}

def binnize(df):

    subdf = []
    for i in range(len(values_in_column_A)):
        subdf.append(df[df['A'] == values_in_column_A[i]])
        subdf[i]['C'] = pd.cut(subdf[i]['B'],bins[values_in_column_A[i]])

    return pd.concat(subdf)

Это работает, но я не думаю, что это достаточно элегантно, я также предвижу некоторые проблемы со скоростью или памятью в производстве, когда у меня будут кадры с миллионами строк. Говоря прямо, я думаю, что это можно было бы сделать лучше.

Я буду благодарен за любую помощь или идеи...
1 2

1 ответ:

Решает ли это вашу проблему?

df = pd.DataFrame({'A' : ['one', 'one', 'two', 'three'] * 3,
                   'B' : np.random.randn(12)})
bins = {'one': (-10,-1,0,1,10), 'two':(-100,0,100), 'three':(-999,0,1,2,3)}

def func(row):
    return pd.cut([row['B']], bins=bins[row['A']])[0]

df['C'] = df.apply(func, axis=1)

Возвращает фрейм данных:

        A         B          C
0     one  1.440957    (1, 10]
1     one  0.394580     (0, 1]
2     two -0.039619  (-100, 0]
3   three -0.500325  (-999, 0]
4     one  0.497256     (0, 1]
5     one  0.342222     (0, 1]
6     two -0.968390  (-100, 0]
7   three -0.772321  (-999, 0]
8     one  0.803178     (0, 1]
9     one  0.201513     (0, 1]
10    two  1.178546   (0, 100]
11  three -0.149662  (-999, 0]

Более быстрая версия binnize:

def binize2(df):
    df['C'] = ''
    for key, values in bins.items():
        mask = df['A'] == key
        df.loc[mask, 'C'] = pd.cut(df.loc[mask, 'B'], bins=values)

%%timeit
df3 = binnize(df1)
10 loops, best of 3: 56.2 ms per loop

%%timeit
binize2(df2)
100 loops, best of 3: 6.64 ms per loop
Это, вероятно, связано с тем, что он изменяет фрейм данных inplace и не создает новый.