3
votes

Dans la méthode d'application pandas, dupliquez la ligne en fonction de la condition

Voici un exemple de mon df:

def f(row):
   if condition:
      row["a"] = 3
   elif condition:
      row["a"] = 4
   elif condition:
      row_duplicated = row.copy()
      row_duplicated["a"] = 5 # I need also this row to be included in the df

   return row
df.apply(f, axis=1)

Et je veux arriver à ceci:

pd.DataFrame([["1", "2"], ["1", "2"], ["3", "other_value"], ["3", "row_duplicated_with_edits_in_this_column"]],
                     columns=["a", "b"])
    a   b
0   1   2
1   1   2
2   3   other_value
3   3   row_duplicated_with_edits_in_this_column

La règle est d'utiliser la méthode apply, effectuez quelques vérifications (pour garder l'exemple simple, je n'inclus pas ces vérifications), mais sous certaines conditions, pour certaines lignes de la fonction apply, dupliquez la ligne, modifiez la ligne et insérez les deux lignes dans le df.

Donc quelque chose comme:

pd.DataFrame([["1", "2"], ["1", "2"], ["3", "other_value"]],
                     columns=["a", "b"])
    a   b
0   1   2
1   1   2
2   3   other_value

Je ne veux pas stocker les lignes dupliquées quelque part dans ma classe et les ajouter à la fin. Je veux le faire à la volée.

J'ai vu ceci pandas: applique une fonction à DataFrame qui peut renvoyer plusieurs lignes mais je ne sais pas si groupby peut m'aider ici.

Merci


0 commentaires

3 Réponses :


2
votes

Voici une façon d'utiliser df.iterrows dans une compréhension de liste. Vous devrez ajouter vos lignes à une boucle, puis concaténer.

def func(row):
   if row['a'] == "3":
        row2 = row.copy()
        # make edits to row2
        return pd.concat([row, row2], axis=1)
   return row

pd.concat([func(row) for _, row in df.iterrows()], ignore_index=True, axis=1).T

   a            b
0  1            2
1  1            2
2  3  other_value
3  3  other_value

J'ai trouvé que dans mon cas, c'est mieux sans ignore_index = True car je plus tard lors de la fusion de 2 dfs.


1 commentaires

merci, cela fonctionne, j'aurais préféré utiliser apply (), car j'utilise df.query (). apply (). combine_first () , mais toujours avec de petites modifications, votre solution fonctionne sans avoir à stocker le données n'importe où.



0
votes

Je le vectoriserais, en le faisant catégorie par catégorie:

df[df_condition_1]["a"] = 3
df[df_condition_2]["a"] = 4

duplicates = df[df_condition_3] # somehow we store it ?     
duplicates["a"] = 5 

#then 
df.join(duplicates, how='outer')

Cette solution répond-elle à vos besoins?


1 commentaires

merci, ce serait plus rapide, mais oui, mes conditions sont nombreuses et à travers plusieurs fonctions, donc ce genre de solution rendra le code moins lisible.



2
votes

Votre logique semble généralement vectorisable. Comme l'ordre des lignes dans votre sortie semble être important, vous pouvez incrémenter le RangeIndex par défaut de 0,5, puis utiliser sort_index .

def row_appends(x):
    newrows = x.loc[x['a'].isin(['3', '4', '5'])].copy()
    newrows.loc[x['a'] == '3', 'b'] = 10  # make conditional edit
    newrows.loc[x['a'] == '4', 'b'] = 20  # make conditional edit
    newrows.index = newrows.index + 0.5
    return newrows

res = pd.concat([df, df.pipe(row_appends)])\
        .sort_index().reset_index(drop=True)

print(res)

   a            b
0  1            2
1  1            2
2  3  other_value
3  3           10

p>


0 commentaires