Pandas - Remplacer la valeur de ligne du calcul dérivé


Bill Armstrong

J'ai besoin de faire un inplaceremplacement de valeur basé sur une valeur d'index de ligne. La valeur de remplacement est un calcul de trame de données (ligne et colonne) en tranches.

Installer

In [1]: import pandas as pd                                                                                                  

In [2]: cols = [0, 1, 'A0', 'A1', 'A2', 'A3', 'B0', 'B1', 'B2', 'B3']                                                        

In [3]: data = [['sum', 4531.0010, 0, 0, 0, 2, 0, 0, 0, 7], 
   ...:         ['', 4531.0010, 5, 6, 3, 0, 5, 4, 7, 0], 
   ...:         ['', 4531.0010, 1, 3, 9, 0, 2, 2, 3, 0], 
   ...:         ['sum', 5037.0022, 0, 0, 0, 8, 0, 0, 0, 5], 
   ...:         ['', 5037.0022, 2, 2, 3, 0, 1, 3, 9, 0], 
   ...:         ['', 5037.0022, 5, 4, 7, 0, 5, 6, 3, 0]]                                                                     

In [4]: df = pd.DataFrame(data=data, columns=cols)                                                                           

In [5]: df = df.set_index(list(df.columns[[0, 1]]))                                                                          

In [6]: df                                                                                                                   
Out[6]: 
               A0  A1  A2  A3  B0  B1  B2  B3
0   1                                        
sum 4531.0010   0   0   0   2   0   0   0   7
    4531.0010   5   6   3   0   5   4   7   0
    4531.0010   1   3   9   0   2   2   3   0
sum 5037.0022   0   0   0   8   0   0   0   5
    5037.0022   2   2   3   0   1   3   9   0
    5037.0022   5   4   7   0   5   6   3   0

Comme vous pouvez le voir, la ligne est multiindexée avec index = 1 est un nombre qui représente un sous-ensemble des données. Dans chaque sous-ensemble de données, il y a une "somme" dans index = 0 que je voudrais "distribuer" vers le haut (ou vers le bas) aux soldes nuls.

Le calcul se compose essentiellement des colonnes "A" et des lignes d'index 1 avec la même valeur à additionner à un dénominateur. Ensuite, la somme de la ligne pour ce groupe de données est le numérateur. Le rapport est ensuite utilisé pour répartir la somme entre les lignes.

Pour les lignes = 4531,0010 et les colonnes avec un A, il serait calculé comme suit:

(5 + 6 + 3) / (5 + 6 + 3 + 1 + 3 + 9) * 2 = ligne 1, colonne A3
(1 + 3 + 9) / (5 + 6 + 3 + 1 + 3 + 9) * 2 = ligne 2, colonne A3

Le résultat dfressemblerait à ceci:

Out[7]: 
               A0  A1  A2     A3  B0  B1  B2     B3
0   1                                              
sum 4531.0010   0   0   0  2.000   0   0   0  7.000
    4531.0010   5   6   3  1.037   5   4   7  4.870
    4531.0010   1   3   9  0.923   2   2   3  2.130
sum 5037.0022   0   0   0  8.000   0   0   0  5.000
    5037.0022   2   2   3  2.435   1   3   9  2.407
    5037.0022   5   4   7  5.565   5   6   3  2.593

Le nombre de lignes n'est pas fixe - il peut y en avoir une ou 10.

Ce que j'ai essayé

J'ai essayé d'utiliser des variantes de .pivot_table()mais je n'arrive pas à comprendre comment inverser le processus en utilisant la division. A titre d' exemple .

J'ai également utilisé des variantes de .sum()mais essayer de contraindre l' dfutilisation de tranches m'échappe. Un des nombreux exemples .

Je pense que je peux faire fonctionner cela avec beaucoup de fonctions python, mais il semble que cela devrait être possible plus efficacement. Toute direction est grandement appréciée.

Jezreel

Solution fonctionnant, si premiers niveaux uniques de MultiIndex:

cols = [0, 1, 'A0', 'A1', 'A2', 'A3', 'B0', 'B1', 'B2', 'B3']

data = [['sum1', 4531.0010, 0, 0, 0, 2, 0, 0, 0, 7], 
        ['sum1', 4531.0010, 5, 6, 3, 0, 5, 4, 7, 0], 
        ['sum1', 4531.0010, 1, 3, 9, 0, 2, 2, 3, 0], 
        ['sum2', 5037.0022, 0, 0, 0, 8, 0, 0, 0, 5], 
        ['sum2', 5037.0022, 2, 2, 3, 0, 1, 3, 9, 0], 
        ['sum2', 5037.0022, 5, 4, 7, 0, 5, 6, 3, 0]]

df = pd.DataFrame(data=data, columns=cols)

df = df.set_index(list(df.columns[[0, 1]]))
print (df)
                A0  A1  A2  A3  B0  B1  B2  B3
0    1                                        
sum1 4531.0010   0   0   0   2   0   0   0   7
     4531.0010   5   6   3   0   5   4   7   0
     4531.0010   1   3   9   0   2   2   3   0
sum2 5037.0022   0   0   0   8   0   0   0   5
     5037.0022   2   2   3   0   1   3   9   0
     5037.0022   5   4   7   0   5   6   3   0

#loop by first letters of values in columns
for c in df.columns.str[0].unique():
    #filter values by first letter
    df1 = df.filter(like=c)
    #get sum per rows
    s = df1.iloc[:, :-1].sum(axis=1)
    #get last column
    last_col = df1.iloc[:, -1]
    #replace 0 in last column to previous non 0
    last_col = last_col.mask(last_col == 0).ffill()
    #divide by sum per first level with multiple by last_col
    s = s.div(s.sum(level=0), level=0).mul(last_col)
    #add to last column
    df[last_col.name] += s
print (df)
                A0  A1  A2        A3  B0  B1  B2        B3
0    1                                                    
sum1 4531.0010   0   0   0  2.000000   0   0   0  7.000000
     4531.0010   5   6   3  1.037037   5   4   7  4.869565
     4531.0010   1   3   9  0.962963   2   2   3  2.130435
sum2 5037.0022   0   0   0  8.000000   0   0   0  5.000000
     5037.0022   2   2   3  2.434783   1   3   9  2.407407
     5037.0022   5   4   7  5.565217   5   6   3  2.592593

Articles connexes


Calcul de la valeur décalée des pandas

Wookeun lee J'aimerais créer une nouvelle colonne, contenant des valeurs calculées à partir de la valeur décalée dans d'autres colonnes. Comme vous le voyez dans le code ci-dessous, j'ai d'abord créé une série chronologique de données. Le « prix » est constitu