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
.
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
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
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.
Au lieu d'interrogation, vous pouvez utiliser pipe
:
s.pipe(lambda x: x[x>0]).pipe(lambda x: x[x<10])