1
votes

Les pandas pivotent avec plusieurs éléments par colonne, comment éviter de les agréger?

Suivez cette question , en particulier ce commentaire .

Considérez le dataframe suivant:

Belonging   Bike           Car           House
Person
  Adam       NaN        [10.0]  [300.0, 180.0]
 Cesar       NaN        [12.0]             NaN
 Diana     [2.0]  [15.0, 21.0]         [450.0]
 Erika       NaN        [11.0]         [600.0]

À quoi ressemble comme ceci:

df_pivot = df.pivot_table(
    values='Value', 
    index='Person', 
    columns='Belonging',
    aggfunc=list,
)

L'utilisation d'un pivot_table () est un bon moyen de remodeler ces données qui permettra de les interroger par personne et de voir toutes leurs effets personnels sur une seule ligne, ce qui permet de répondre très facilement à des requêtes telles que "Comment trouver la valeur de la voiture des personnes, si leur maison a une valeur supérieure à 400,0?"

A pivot_table () peut être facilement construit pour cet ensemble de données avec:

Belonging   Bike     Car    House
Person
  Adam       NaN    10.0    240.0
 Cesar       NaN    12.0      NaN
 Diana       2.0    18.0    450.0
 Erika       NaN    11.0    600.0

Ce qui ressemblera à:

  Person Belonging  Value
0   Adam     House  300.0
1   Adam       Car   10.0
2  Cesar       Car   12.0
3  Diana     House  450.0
4  Diana       Car   15.0
5  Diana      Bike    2.0
6  Erika     House  600.0
7  Erika       Car   11.0
8  Diana       Car   21.0
9   Adam     House  180.0

Mais cela est limité lorsqu'une personne a plus d'un du même type d'appartenance, par exemple deux voitures, deux maisons ou deux vélos.

Tenez compte des données mises à jour:

df = pd.DataFrame({
    'Person': ['Adam', 'Adam', 'Cesar', 'Diana', 'Diana', 'Diana', 'Erika', 'Erika', 'Diana', 'Adam'],
    'Belonging': ['House', 'Car', 'Car', 'House', 'Car', 'Bike', 'House', 'Car', 'Car', 'House'],
    'Value': [300.0, 10.0, 12.0, 450.0, 15.0, 2.0, 600.0, 11.0, 21.0, 180.0],
})
  • Permettre d'interroger facilement les biens d'une personne sur une seule ligne, comme le fait pivot_table () ;
  • Préserver les détails des personnes qui ont plusieurs biens d'un certain type;
  • Utilisez des colonnes et des types de données Pandas natifs qui permettent d'utiliser les méthodes Pandas pour interroger et résumer les données.

J'ai pensé à mettre à jour les descriptions d'appartenance pour inclure un compteur, tel que "Maison 1", "Voiture 2", etc. Peut-être trier pour que le plus précieux vienne en premier (pour aider à répondre à des questions telles que "possède une maison valant plus de 400,0" en regardant uniquement "Maison 1".)

Ou peut-être en utilisant un pd.MultiIndex pour pouvoir accéder à toutes les colonnes "Maison"

Mais je ne sais pas comment remodeler les données de cette manière.

Ou y a-t-il de meilleures suggestions sur la façon de les remodeler (autre que d'ajouter un décompte par appartenance) qui conserver les fonctionnalités décrites ci-dessus? Comment le remodeleriez-vous et comment répondriez-vous à toutes ces questions que j'ai mentionnées ci-dessus?


0 commentaires

4 Réponses :


1
votes

En utilisant groupby , vous pourriez réaliser quelque chose comme ceci.

df.pivot('Person', 'Belonging')

                Value
Belonging      Bike_1   Car_1   Car_2   House_1   House_2
Person                  
Adam             NaN    10.0      NaN     300.0     180.0
Cesar            NaN    12.0      NaN       NaN       NaN
Diana            2.0    15.0     21.0     450.0       NaN
Erika            NaN    11.0      NaN     600.0       NaN

ce qui donnerait.

df['Belonging'] = df["Belonging"] + "_" + df.groupby(['Person','Belonging']).cumcount().add(1).astype(str)

  Person    Belonging   Value
0   Adam      House_1   300.0
1   Adam        Car_1   10.0
2   Cesar       Car_1   12.0
3   Diana     House_1   450.0
4   Diana       Car_1   15.0
5   Diana      Bike_1   2.0
6   Erika     House_1   600.0
7   Erika       Car_1   11.0
8   Diana       Car_2   21.0
9   Adam      House_2   180.0

Vous pouvez définir vos propres fonctions dans la méthode .agg pour fournir des descriptions plus appropriées également.


Modifier

Vous pouvez également essayer p>

                      Value
                        sum  count     min     max
Person  Belonging               
Adam    Car            10.0      1    10.0    10.0
        House         480.0      2   180.0   300.0
Cesar   Car            12.0      1    12.0    12.0
Diana   Bike            2.0      1     2.0     2.0
        Car            36.0      2    15.0    21.0
        House         450.0      1   450.0   450.0
Erika   Car            11.0      1    11.0    11.0
        House         600.0      1   600.0   600.0

Ensuite, vous pouvez simplement utiliser pivot

df_new = df.groupby(['Person', 'Belonging']).agg(('sum', 'count', 'min', 'max'))


2 commentaires

Ce résumé est certainement utile, mais ne permet pas vraiment (ou même possible?) De répondre à des questions telles que "Comment trouver la valeur de la voiture des personnes, si leur maison a une valeur supérieure à 400,0?"


Ajout de quelques modifications pour répondre à cette question. Bien que je pense que @SpghttCd a une bonne solution



1
votes

Peut-être comme ceci:

étant donné votre tableau croisé dynamique dans le cadre de données suivant:

new_pv.filter(like='House').sum().sum()

# 1530.0

puis appliquez pd.Series à toutes les colonnes .
Pour nommer correctement les colonnes, calculez la longueur maximale des listes dans chaque colonne, puis utilisez 'set_axis' pour renommer:

new_pv.filter(like='House').count(1)

# Person
# Adam     2
# Cesar    0
# Diana    1
# Erika    1
# dtype: int64

comptage des maisons:

XXX

somme de toutes les valeurs de la maison:

new_pv =  pd.DataFrame(index=pv.index)
for col in pv:
    n = int(pv[col].str.len().max())
    new_pv = pd.concat([new_pv, pv[col].apply(pd.Series).set_axis([f'{col}_{i}' for i in range(n)], 1, inplace = False)], 1)


#         Bike_0  Car_0  Car_1  House_0  House_1
# Person                                        
# Adam       NaN   10.0    NaN    300.0    180.0
# Cesar      NaN   12.0    NaN      NaN      NaN
# Diana      2.0   15.0   21.0    450.0      NaN
# Erika      NaN   11.0    NaN    600.0      NaN


2 commentaires

Oui, c'est sympa! Bien que pour répondre à la valeur totale de toutes les maisons, je devrais parcourir les colonnes pour déterminer le nombre de maisons que l'on pourrait avoir ... Par exemple, pour trouver new_pv.House_0.sum () + new_pv.House_1.sum () qui devrait donner 1530.0


ou new_pv.filter (like = 'House'). sum (). sum ()



0
votes

J'ai essayé de faire cela avec les listes du dataframe, afin qu'elles soient converties en ndarrays.

pd_df_pivot = df_pivot.copy(deep=True)
for row in range(0,df_pivot.shape[0]):
    for col in range(0,df_pivot.shape[1]):
        if type(df_pivot.iloc[row,col]) is list:
            pd_df_pivot.iloc[row,col] = np.array(df_pivot.iloc[row,col])
        else:
            pd_df_pivot.iloc[row,col] = df_pivot.iloc[row,col]  


2 commentaires

Idée intéressante! Mais un moyen plus simple d'accomplir cela est d'appeler directement pivot_table () avec aggfunc = np.array . Cela fait, il est possible d'obtenir la valeur totale des maisons avec df_pivot ['House']. Apply (np.sum) .sum () . Prometteur, mais je pense que les colonnes Pandas sont toujours meilleures que les ndarrays dans les cellules ici.


Je pense que vous auriez pensé à aggfunc = pd.DataFrame, j'essaie de trouver comment vous pouvez répondre "Comment trouver la valeur de la voiture des personnes, si elles ont une maison évaluée à plus de 400,0"



1
votes

J'ai fini par trouver une solution à celle-ci, inspirée des excellentes réponses de @SpghttCd et @ Josmoor98 , mais avec quelques différences:

  • En utilisant un MultiIndex, j'ai donc un moyen très simple d'obtenir toutes les maisons ou toutes les voitures.
  • Le tri des valeurs, donc en regardant la première maison ou voiture peut être utilisé pour savoir qui en a une qui vaut plus que X.

Code pour le tableau croisé dynamique:

df_pivot.loc['Adam'].dropna()

DataFrame résultant:

df_pivot['House'].sum()

Les requêtes sont assez simples .

Par exemple, trouver la valeur de la voiture de la personne s , si sa maison a une valeur supérieure à 400,0:

df_pivot.loc[
    df_pivot[('House', 1)] > 400.0,
    'Car'
].stack().mean()

Résultat:

BelongingNo      1      2
Person
 Diana        21.0   15.0
 Erika        11.0    NaN

Le prix moyen de la voiture pour eux:

df_pivot.loc[
    df_pivot[('House', 1)] > 400.0,
    'Car'
]

Résultat: 15,6666

Ici, utiliser stack () est un moyen puissant d'aplatir le deuxième niveau du MultiIndex, après avoir utilisé le niveau supérieur pour sélectionner une colonne Appartenance.

La même chose est utile pour obtenir la valeur totale de toutes les maisons:

Belonging     Bike     Car           House
BelongingNo    1         1      2        1      2
Person
  Adam         NaN    10.0    NaN    300.0  180.0
 Cesar         NaN    12.0    NaN      NaN    NaN
 Diana         2.0    21.0   15.0    450.0    NaN
 Erika         NaN    11.0    NaN    600.0    NaN

Résultats dans les 1530,0 attendus.

Enfin, en regardant tous les biens d'une seule personne: p >

df_pivot = (df
    .assign(BelongingNo=df
        .sort_values(by='Value', ascending=False)
        .groupby(['Person', 'Belonging'])
        .cumcount() + 1
    )
    .pivot_table(
        values='Value', 
        index='Person', 
        columns=['Belonging', 'BelongingNo'],
    )
)

Renvoie les deux maisons attendues et la voiture, avec leurs valeurs respectives.


0 commentaires