web-dev-qa-db-fra.com

Existe-t-il une méthode de requête ou similaire pour pandas Series (pandas.Series.query ())?

La méthode pandas.DataFrame.query() est très utile pour filtrer (avant/après) les données lors du chargement ou du traçage. C'est particulièrement pratique pour le chaînage de méthodes.

Je souhaite souvent appliquer la même logique à un pandas.Series, par exemple. après avoir utilisé une méthode telle que df.value_counts qui renvoie un pandas.Series.

Exemple

Supposons qu'il y a un énorme tableau avec les colonnes Player, Game, Points et que je veux tracer un histogramme des joueurs avec plus de 14 fois 3 points. Je dois d’abord faire la somme des points de chaque joueur (groupby -> agg), ce qui donnera une série de ~ 1000 joueurs et leurs points globaux. En appliquant la logique .query, cela ressemblerait à ceci:

df = pd.DataFrame({
    'Points': [random.choice([1,3]) for x in range(100)], 
    'Player': [random.choice(["A","B","C"]) for x in range(100)]})

(df
     .query("Points == 3")
     .Player.values_count()
     .query("> 14")
     .hist())

Les seules solutions que je trouve me forcent à faire une tâche inutile et à briser la méthode de chaînage:

(points_series = df
     .query("Points == 3")
     .groupby("Player").size()
points_series[points_series > 100].hist()

Le chaînage de méthodes ainsi que la méthode de requête aident à garder le code lisible, tandis que le filtrage de sous-ensembles peut devenir très rapidement compliqué.

# just to make my point :)
series_bestplayers_under_100[series_prefiltered_under_100 > 0].shape

S'il vous plaît, aidez-moi à sortir de mon dilemme! Merci

12
dmeu

IIUC vous pouvez ajouter query("Points > 100"):

df = pd.DataFrame({'Points':[50,20,38,90,0, np.Inf],
                   'Player':['a','a','a','s','s','s']})

print (df)
  Player     Points
0      a  50.000000
1      a  20.000000
2      a  38.000000
3      s  90.000000
4      s   0.000000
5      s        inf

points_series = df.query("Points < inf").groupby("Player").agg({"Points": "sum"})['Points']
print (points_series)     
a = points_series[points_series > 100]
print (a)     
Player
a    108.0
Name: Points, dtype: float64


points_series = df.query("Points < inf")
                  .groupby("Player")
                  .agg({"Points": "sum"})
                  .query("Points > 100")

print (points_series)     
        Points
Player        
a        108.0

Une autre solution est Selection By Callable :

points_series = df.query("Points < inf")
                  .groupby("Player")
                  .agg({"Points": "sum"})['Points']
                  .loc[lambda x: x > 100]

print (points_series)     
Player
a    108.0
Name: Points, dtype: float64

Réponse modifiée par question modifiée:

np.random.seed(1234)
df = pd.DataFrame({
    'Points': [np.random.choice([1,3]) for x in range(100)], 
    'Player': [np.random.choice(["A","B","C"]) for x in range(100)]})

print (df.query("Points == 3").Player.value_counts().loc[lambda x: x > 15])
C    19
B    16
Name: Player, dtype: int64

print (df.query("Points == 3").groupby("Player").size().loc[lambda x: x > 15])
Player
B    16
C    19
dtype: int64
6
jezrael

Pourquoi ne pas convertir Series en DataFrame, effectuer une interrogation, puis reconvertir. 

df["Points"] = df["Points"].to_frame().query('Points > 100')["Points"]

Ici, .to_frame() est converti en DataFrame, tandis que le ["Points"] final est converti en série.

La méthode .query() peut ensuite être utilisée de la même manière, que l'objet Pandas comporte une ou plusieurs colonnes.

3
Martin

Au lieu d'interrogation, vous pouvez utiliser pipe:

s.pipe(lambda x: x[x>0]).pipe(lambda x: x[x<10])
0
Ilya Prokin