Как выбрать из панд DataFrame n строк из каждой подгруппы равномерно - PullRequest
0 голосов
/ 23 июня 2019

В панде Dataframe у меня есть подгруппы с разным и большим количеством строк.Я хочу уменьшить количество строк для предварительного анализа, одновременно гарантируя, что данные все еще будут репрезентативными во всем диапазоне.

Я провел моделирование с 2-факторами или параметрами ('A','B') и 2-уровнямиили значения на коэффициент ('A1','A2','B1','B2').Каждое моделирование соответствует комбинации значений 'A','B'.Симуляция останавливается после того, как счетчик превысил определенное число («35» в приведенном ниже примере).Для каждой симуляции счетчик и его увеличение различны.И на каждом шаге значение 'eval' суммируется из моделирования.

В приведенном ниже примере показан пример результата моделирования.Теперь симуляция на самом деле выполняется гораздо дольше (скажем, для примера, пока она не превысит 10000), и для построения графика изменения значений eval в моем предварительном анализе требуются часы.

Этот кодгенерирует выборку результатов моделирования:

import pandas as pd
import numpy as np

columns = ['FactorA', 'FactorB', 'step']
data = [['A1', 'B1', 8], ['A1', 'B1', 13], ['A1', 'B1', 18], ['A1', 'B1', 23], ['A1', 'B1', 28], ['A1', 'B1', 33], ['A1', 'B1', 38],
        ['A1', 'B2', 7], ['A1', 'B2', 13],['A1', 'B2', 19],['A1', 'B2', 25],['A1', 'B2', 31],['A1', 'B2', 37],
        ['A2', 'B1', 6], ['A2', 'B1', 14],['A2', 'B1', 22],['A2', 'B1', 30],['A2', 'B1', 38],
        ['A2', 'B2', 10], ['A2', 'B2', 12],['A2', 'B2', 14],['A2', 'B2', 16],['A2', 'B2', 18],['A2', 'B2', 20],['A2', 'B2', 22],['A2', 'B2', 24],['A2', 'B2', 26],['A2', 'B2', 28],['A2', 'B2', 30],['A2', 'B2', 32],['A2', 'B2', 34],['A2', 'B2', 36]
       ]
df = pd.DataFrame(data, columns=columns)
df['eval'] = np.random.randint(1, 6, df.shape[0])

Я пробовал это, но, хотя он сокращает количество точек данных, он не уравновешивает количество точек данных за одно моделирование:

df_reduced = df.iloc[::2]

Также попытался:

df_reduced = df.sample(n=int(len(df)/6))

, но он также не уравновешивает количество точек данных за симуляцию.

Мне нужен DataFrame, в котором каждая подгруппа имеет одинаковое количество строк.Чтобы обеспечить сбалансированность выбора или выборки, я хочу, чтобы разделение для каждой подгруппы с использованием .iloc учитывало шаги, обеспечивающие выбор 'n' членов для каждой подгруппы.Было бы замечательно, но не обязательно включать первый и последний ряд каждой подгруппы.

Ответы [ 2 ]

0 голосов
/ 24 июня 2019

После ответа от @Valdi_Bo я попал на страницу Группировать по: split-apply-Объединить и смешал идеи в здесь , здесь , здесь , здесь и здесь , чтобы получить не элегантное, но рабочее решение.

Для примера в примере мы можемподсчитать количество строк в группе:

grouped = df.groupby(['FactorA','FactorB'])
grouped.size()

Это приведет к:

FactorA  FactorB
A1       B1          7
         B2          6
A2       B1          5
         B2         14
dtype: int64

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

def filter_group(dfg, col, qty):
    col_min = dfg[col].min()
    col_max = dfg[col].max()
    col_length = dfg[col].size
    jumps = col_length-1
    jump_size = int((col_max - col_min) / jumps)
    new_jump_size =  jumps/qty
    if new_jump_size > 1:
        new_jump_size = int(new_jump_size)*jump_size
    else:
        new_jump_size = jump_size

    col_select = list(range(col_min, col_max, new_jump_size))
    col_select.append(col_max)

    return dfg[dfg[col].isin(col_select)]
grouped = df.groupby(['FactorA','FactorB'], group_keys=False).apply(lambda x: filter_group(x,'step',3))

Мы можем проверить количество строк для демонстрационного кадра данных:

grouped = grouped.groupby(['FactorA','FactorB'])
grouped.size()

Это дает:

FactorA  FactorB
A1       B1         4
         B2         6
A2       B1         5
         B2         5
dtype: int64

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

0 голосов
/ 23 июня 2019

Посмотрите на следующую инструкцию в вашем посте:

df.sample(n=int(len(df)))

Что здесь странного:

  • int не требуется ( len уже имеет тип int ).
  • len (df) выбирает all строк из df , поэтому ваш "образец" создан таким образом содержит полный оригинал df , только перетасовывает заказ. Это то, что вы хотите?

А что касается групповой балансировки:

Решите, как вы хотите сохранить баланс:

  • Вариант 1: равен числу строк выборки из каждой группы.
  • Вариант 2: равен фракция выборок из каждой группы.

Когда вы решите:

  • запустите groupby вашего исходного DataFrame по групповому критерию,
  • применение функции, возвращающей соответствующий образец из текущей группы.

Пример: если вы хотите выбрать из df образец 2 строк из каждая группа (вариант 1), запустить:

df.groupby(['FactorA', 'FactorB']).apply(lambda grp: grp.sample(n=2))

Если вы хотите вернуться к исходному (одноуровневому) индексу, добавьте:

.reset_index(level=[0, 1], drop=True)

к вышеуказанной инструкции.

Если вы предпочитаете вариант 2 (дробь), измените n = ... на frac = ... .

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