Как сделать выборку из DataFrame на основе процентиля столбца?

Учитывая такой набор данных:

import pandas as pd

rows = [{'key': 'ABC', 'freq': 100}, {'key': 'DEF', 'freq': 60}, 
{'key': 'GHI', 'freq': 50}, {'key': 'JKL', 'freq': 40}, 
{'key': 'MNO', 'freq': 13}, {'key': 'PQR', 'freq': 11}, 
{'key': 'STU', 'freq': 10}, {'key': 'VWX', 'freq': 10}, 
{'key': 'YZZ', 'freq': 3}, {'key': 'WHYQ', 'freq': 3}, 
{'key': 'HOWEE', 'freq': 2}, {'key': 'DUH', 'freq': 1}, 
{'key': 'HAHA', 'freq': 1}]

df = pd.DataFrame(rows)

df['percent'] = df['freq'] / sum(df['freq'])

[вне]:

key freq    percent
0   ABC 100 0.328947
1   DEF 60  0.197368
2   GHI 50  0.164474
3   JKL 40  0.131579
4   MNO 13  0.042763
5   PQR 11  0.036184
6   STU 10  0.032895
7   VWX 10  0.032895
8   YZZ 3   0.009868
9   WHYQ    3   0.009868
10  HOWEE   2   0.006579
11  DUH 1   0.003289
12  HAHA    1   0.003289

Цель состоит в том, чтобы

  1. выберите 1 пример из верхних 50-100 процентилей частоты
  2. выберите 2 примера из процентиля 10-50 и
  3. выберите 4 примера из < 10 процентиля

В этом случае подходит ответ:

  1. Выберите 1 из ['ABC', 'DEF']
  2. Выберите 2 из ['GHI', 'JKL', 'MNO', 'PQR']
  3. Выберите 4 из ['VWX', 'STU', 'YZZ', 'WHYQ', 'HOWEE', 'HAHA', 'DUH']

Я пробовал это:

import random
import pandas as pd

rows = [{'key': 'ABC', 'freq': 100}, {'key': 'DEF', 'freq': 60}, 
{'key': 'GHI', 'freq': 50}, {'key': 'JKL', 'freq': 40}, 
{'key': 'MNO', 'freq': 13}, {'key': 'PQR', 'freq': 11}, 
{'key': 'STU', 'freq': 10}, {'key': 'VWX', 'freq': 10}, 
{'key': 'YZZ', 'freq': 3}, {'key': 'WHYQ', 'freq': 3}, 
{'key': 'HOWEE', 'freq': 2}, {'key': 'DUH', 'freq': 1}, 
{'key': 'HAHA', 'freq': 1}]

df = pd.DataFrame(rows)
df['percent'] = df['freq'] / sum(df['freq'])

bin_50_100 = []
bin_10_50 = []
bin_10 = []

total_percent = 1.0
for idx, row in df.sort_values(by=['freq', 'key'], ascending=False).iterrows():
    if total_percent > 0.5:
        bin_50_100.append(row['key'])
    elif 0.1 < total_percent < 0.5:
        bin_10_50.append(row['key'])
    else:
        bin_10.append(row['key'])
    total_percent -= row['percent']

    
    
print(random.sample(bin_50_100, 1))
print(random.sample(bin_10_50, 2))
print(random.sample(bin_10, 4))

[вне]:

['DEF']
['MNO', 'PQR']
['HOWEE', 'WHYQ', 'HAHA', 'DUH']

Но есть ли более простой способ решить проблему?

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
8
0
493
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Давай попробуем:

bins = [0, 0.1, 0.5, 1]
samples = [3,3,1]

df['sample'] = pd.cut(df.percent[::-1].cumsum(),  # accumulate percentage
                              bins=[0, 0.1, 0.5, 1],      # bins
                              labels=False             # num samples 
                             ).astype(int)


df.groupby('sample').apply(lambda x: x.sample(n=samples[x['sample'].iloc[0])] )

Выход:

             key  freq   percent  sample
sample                                  
1      0     ABC   100  0.328947       1
2      2     GHI    50  0.164474       2
       5     PQR    11  0.036184       2
4      7     VWX    10  0.032895       4
       6     STU    10  0.032895       4
       12   HAHA     1  0.003289       4
       10  HOWEE     2  0.006579       4

Ну это при условии, что нет. образцов уникальны. т.е. 1 2 4, но в противном случае выдаст ошибку Categorical categories must be unique.

alvas 09.12.2020 19:39

Спасибо за обновление!! Почему это samples = np.array([3,3,1])?

alvas 09.12.2020 19:45

Это для предварительной индексации массива numpy. Передача label=False в cuts возвращает номера бинов. например [0, 1, 2, 0, 1, 1]. Затем мы используем индексацию numpy для нарезки соответствующего количества выборок.

Quang Hoang 09.12.2020 19:47

Я получаю другой результат от вашего.

alvas 09.12.2020 19:54

С [3, 3, 1] я получаю только 2 выборки бинов, а не 3 =(

alvas 09.12.2020 19:55

@alvas ах, да, глупый я. Обновил ответ. Нам не нужно делать np.array для samples.

Quang Hoang 09.12.2020 19:59

Я думаю, что в последнем grouby есть какая-то синтаксическая ошибка, но я думаю, что понял идею =) Спасибо!!

alvas 09.12.2020 20:06
df.groupby('sample').apply(lambda x: x.sample(n=samples[x['sample'].iloc[0]])) =)
alvas 09.12.2020 20:08

Посмотрите, поможет ли это.

df = pd.DataFrame(rows)
df['percent'] = df['freq'] / sum(df['freq'])

s = list(1 - df['percent'].cumsum())
s.pop(-1)
s.insert(0,1.0)
df['cum_lag'] = s

print(df[df['cum_lag'] > 0.5]['key'])

Другие вопросы по теме