1
votes

Ajouter une colonne à la trame de données pandas avec un calcul entre la valeur et la condition des lignes

Je dois ajouter une nouvelle colonne ("delta") à un dataframe en calculant la différence entre chaque valeur de ligne dans la colonne "value" et la valeur dans la même colonne quand num = 5, avec la même couleur et pour chaque grouper. Le résultat devrait être le suivant:

import plotly.graph_objs as go
import pandas as pd

d = {
    "group" : ["Group1", "Group1", "Group1", "Group1", "Group1", "Group1", "Group1", "Group1", "Group1", "Group1", "Group1", "Group1", "Group2", "Group2", "Group2", "Group2", "Group2", "Group2", "Group2", "Group2","Group2", "Group2", "Group2", "Group2"],
    "color" : ["red", "green", "blue", "yellow", "red", "green", "blue", "yellow", "red", "green", "blue", "yellow", "red", "green", "blue", "yellow", "red", "green", "blue", "yellow", "red", "green", "blue", "yellow"],
    "num" : [1, 1, 1, 1, 5, 5, 5, 5, 7, 7, 7, 7, 1, 1, 1, 1, 5, 5, 5, 5, 7, 7, 7, 7],
    "value" : [0.1, 0.2, 0.3, 0.6, 0.5, 0.6, 0.7, 0.5, 0.8, 0.9, 0.7, 0.6, 0.1, 0.2, 0.3, 0.6, 0.5, 0.6, 0.7, 0.5, 0.8, 0.9, 0.7, 0.6,]
    }

df = pd.DataFrame(d)

df_pivot = pd.pivot_table(df, values = ["value"], index = ["group", "color", "num"])

df_pivot["delta"] = df_pivot["value"] # what/how should I substract !?

print(df_pivot)

J'ai essayé d'utiliser pivot_table, je suppose que c'est un début, mais je ne vois vraiment pas comment faire ce calcul conditionnel.

Avez-vous une idée de la manière dont cela pourrait être fait?

Code actuel:

group   color   num value   delta

Group1  red     1   0.1     -0.4    (0.1 - 0.5 (0.5 is the value in Group1, color=red and num=5))
Group1  green   1   0.2     -0.4    (0.2 - 0.6 (0.6 is the value in Group1, color=green and num=5))
Group1  blue    1   0.3     -0.4    (0.3 - 0.7)
Group1  yellow  1   0.6     0.1     (0.6 - 0.5)
Group1  red     5   0.5     0
Group1  green   5   0.6     0
Group1  blue    5   0.7     0
Group1  yellow  5   0.5     0
Group1  red     7   0.8     0.3
Group1  green   7   0.9     0.3
Group1  blue    7   0.7     0
Group1  yellow  7   0.6     0.1

Group2  red     1   0.1     etc.


4 commentaires

pouvez-vous s'il vous plaît ajouter la sortie souhaitée


Je l'ai fait dans la première partie de la question


essayez avec df ['dealta'] = df ['value'] -0,5 ; print (df) pas besoin de faire pivoter ce que vous attendiez?


ce n'est pas une constante c'est vraiment une valeur variable en fonction de la condition


3 Réponses :


3
votes

Comme ceci:

In [1771]: m = df[df.num.eq(5)]
In [1774]: res = pd.merge(df,m, on=['group', 'color']) 
In [1779]: res['delta'] = res['value_x'] - res['value_y'] 

In [1781]: res = res.drop(['num_y', 'value_y'],1).rename(columns={'num_x': 'num', 'value_x': 'value'})                                                                                                      

In [1782]: res                                                                                                                                                                                              
Out[1782]: 
     group   color  num  value  delta
0   Group1     red    1    0.1   -0.4
1   Group1     red    5    0.5    0.0
2   Group1     red    7    0.8    0.3
3   Group1   green    1    0.2   -0.4
4   Group1   green    5    0.6    0.0
5   Group1   green    7    0.9    0.3
6   Group1    blue    1    0.3   -0.4
7   Group1    blue    5    0.7    0.0
8   Group1    blue    7    0.7    0.0
9   Group1  yellow    1    0.6    0.1
10  Group1  yellow    5    0.5    0.0
11  Group1  yellow    7    0.6    0.1


5 commentaires

@Yas Cette solution serait robuste. Prendra soin de tous les cas correctement.


Votre réponse était la première et elle est facile à comprendre. Mais je n'aime pas le drop et le renommer à la fin;)


pd.merge ajoute _x et _y dans les noms de colonnes qu'il trouve en double dans les dataframes. Par conséquent, je laisse tomber et renommez. Pas moyen de s'en sortir.


Oui j'ai compris ça, je trouve la réponse d'Andy L. avec où et transformer plus concise.


Génial. C'est votre question et vous avez le droit de choisir la meilleure réponse pour vous-même.



2
votes

créez un dict (groupe, couleur) et mappez-le à chaque ligne.

df['delta'] = (
    df.apply(lambda x: x.value - 
             df.loc[(df.group==x.group) & (df.color==x.color) & (df.num==5)].iloc[0].value, 
             axis=1)
)

ou

d = (
    df.groupby(['group','color'])
    .apply(lambda x: x.loc[x.num.eq(5)].head(1)).value
    .reset_index(2,drop=True)
    .to_dict()
)

df['delta'] = (
    df.apply(lambda x: x.value - d.get((x.group,x.color), x.value), axis=1)
)


2 commentaires

Oh, celui-ci fonctionne aussi. Je ne sais pas encore quel serait le meilleur à utiliser ...


Ma première solution devrait être plus robuste qui gère les cas où il n'y a pas d'autre ligne avec la même couleur et le numéro 5 dans le même groupe.



2
votes

Utilisez where et transform

df['delta'] = (df.value - 
               df.where(df.num.eq(5)).groupby([df.group,df.color])
                                     .value.transform('first'))

Out[16]:
     group   color  num  value  delta
0   Group1     red    1    0.1   -0.4
1   Group1   green    1    0.2   -0.4
2   Group1    blue    1    0.3   -0.4
3   Group1  yellow    1    0.6    0.1
4   Group1     red    5    0.5    0.0
5   Group1   green    5    0.6    0.0
6   Group1    blue    5    0.7    0.0
7   Group1  yellow    5    0.5    0.0
8   Group1     red    7    0.8    0.3
9   Group1   green    7    0.9    0.3
10  Group1    blue    7    0.7    0.0
11  Group1  yellow    7    0.6    0.1
12  Group2     red    1    0.1   -0.4
13  Group2   green    1    0.2   -0.4
14  Group2    blue    1    0.3   -0.4
15  Group2  yellow    1    0.6    0.1
16  Group2     red    5    0.5    0.0
17  Group2   green    5    0.6    0.0
18  Group2    blue    5    0.7    0.0
19  Group2  yellow    5    0.5    0.0
20  Group2     red    7    0.8    0.3
21  Group2   green    7    0.9    0.3
22  Group2    blue    7    0.7    0.0
23  Group2  yellow    7    0.6    0.1


2 commentaires

J'aime vraiment celui-ci, j'essaie juste de comprendre la partie après groupby (value.transform ('first')).


@Yas: il sélectionne le premier non-NaN de valeur de chaque couleur de groupe et remplit cette valeur à l'ensemble du groupe. La commande après le signe moins peut également devenir df.value.where (df.num.eq (5)). Groupby ([df.group, df.color]). Tr‌ ansform ('first') < / code>