Как получить только строки с идентификаторами, которые появляются от 5 до 15 раз в pandas фрейме данных? - PullRequest
0 голосов
/ 11 июля 2020

Пока у меня есть это:

counts = df['ID'].value_counts()
df = df[df['ID'].isin(counts.index[counts > 5])]
counts = df['ID'].value_counts()
df = df[df['ID'].isin(counts.index[counts < 15])]

Но это кажется избыточным, есть ли способ сделать все это в первых двух строках? Если я поставлю (count> 5 и count <15), я получаю сообщение об ошибке: </p>

ValueError: The truth value of a Series is ambiguous. Use a.empty,a.bool(), a.item(), a.any() or a.all()

Ответы [ 3 ]

1 голос
/ 11 июля 2020

Это должно работать:

counts = df['ID'].value_counts()
df = df[df['ID'].isin(counts.index[(counts > 5) & (counts < 15)])]

Ура!

Отредактировано, чтобы включить предложение ALollz !

1 голос
/ 11 июля 2020

USe groupby + transform для широковещательной передачи размера «ID» каждой строке этого идентификатора, затем вы можете создать логическую маску для среза, используя between

import pandas as pd
df = pd.DataFrame({'ID': ['A']*6 +['B']*15 +['C']*5})

df[df.groupby('ID')['ID'].transform('size').between(5, 15, inclusive=False)]

  ID
0  A
1  A
2  A
3  A
4  A
5  A

С точки зрения производительности между использованием groupby + transform или нарезкой индекса с помощью value_counts особой разницы нет. (На самом деле методы value_counts кажутся немного быстрее, если вы думаете, что планируете фильтровать большинство групп (скажем, большинство из них имеют размер 1-2) или если вы планируете оставить большинство групп (большинство из них имеют размер >> 15))

import perfplot
import pandas as pd
import numpy as np

def transform(df):
    return df[df.groupby('ID')['ID'].transform('size').between(5, 15, inclusive=False)]
    
    
def value_counts_slice(df):
    counts = df['ID'].value_counts()
    return df[df['ID'].isin(counts.index[(counts > 5) & (counts < 15)])]
            
            
perfplot.show(
    setup=lambda n: pd.DataFrame({'ID': np.random.randint(0, n, 15*n)}),
    kernels=[
        lambda df: transform(df),
        lambda df: value_counts_slice(df),
    ],
    labels=["Transform", "Value Counts"],
    n_range=[2 ** k for k in range(2,21)],
    equality_check=np.allclose,
    xlabel="Number of ID Groups"
)

введите описание изображения здесь

0 голосов
/ 11 июля 2020

Вы можете сравнить несколько условий в одной операции.

counts = df['ID'].value_counts()
greater_5 = counts.index[counts > 5]
lower_15 = counts.index[counts < 15]
df_filtered = (df[
                 (df['ID'].isin(greater_5)) &
                 (df['ID'].isin(lower_15))
                ]
              )

Вам не нужно создавать маскирующие переменные, но это хороший способ сохранить читабельность кода

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