J'ai un dataframe avec deux colonnes A et B qui contient des listes:
df["C"] = [1, 0, 4, 0]
Je veux construire une troisième colonne C qui est défini de telle sorte qu'il soit égal à la plus petite différence possible entre les éléments de liste dans A et B s'ils ne sont pas vides, et 0 si l'un ou les deux sont vides.
Pour la première ligne, la plus petite différence est 1 (nous prenons la valeur absolue ..), pour la deuxième ligne, il est de 0 car les listes sont vides, la troisième ligne est 4 et la quatrième ligne est à nouveau 0 en raison de une liste vide, nous nous retrouvons donc finalement avec:
import pandas as pd
df = pd.DataFrame({"A" : [[1,5,10], [], [2], [1,2]],
"B" : [[15, 2], [], [6], []]})
5 Réponses :
Je pense que cela fonctionne
def diff(a,b):
if len(a) > 0 and len(b) > 0:
return min([abs(i-j) for i in a for j in b])
return 0
df['C'] = df.apply(lambda x: diff(x.A, x.B), axis=1)
df
A B C
0 [1, 5, 10] [15, 2] 1
1 [] [] 0
2 [2] [6] 4
3 [1, 2] [] 0
Ce n'est pas facilement vectorisable, car vous avez une série de listes object dtype. Vous pouvez utiliser une compréhension de liste avec itertools.product code > :
from itertools import product zipper = zip(df['A'], df['B']) df['C'] = [min((abs(x - y) for x, y in product(*vals)), default=0) for vals in zipper] # alternative: # df['C'] = [min((abs(x - y) for x, y in product(*vals)), default=0) \ # for vals in df[['A', 'B']].values] print(df) # A B C # 0 [1, 5, 10] [15, 2] 1 # 1 [] [] 0 # 2 [2] [6] 4 # 3 [1, 2] [] 0
Est-il possible de convertir la série dtype dans un format différent afin qu'elle puisse être vectorisée?
@ N08, Puisque vous avez des listes de longueur variable, pas facilement, non. NumPy / Pandas fonctionne mieux lorsque chaque dimension a le même nombre de valeurs. Vous pouvez vous retrouver avec beaucoup de valeurs vides, ce qui est également inefficace.
@jpp, gentil, qu'est-ce que la fermeture éclair ici.
Je viens de réaliser que c'était précisément ce que vous aviez dans votre solution :-), laissez-le tel quel avec le ternaire if. Merci pour les conseils quand même
@yatu, pas de problèmes, aime toujours aider :)
Vous pouvez utiliser la compréhension de liste suivante, en vérifiant la différence min du produit cartésien ( itertools.product ) des deux colonnes
[min(abs(i-j) for i,j in product(*a)) if all(a) else 0 for a in df.values] [1, 0, 4, 0]
min peut prendre un itérable (vous pouvez donc utiliser une expression de générateur, aucune création de liste supplémentaire n'est requise). De plus, tolist () n'est pas requis lors de l'itération de df.values . Enfin, min a une valeur par défaut qui supprime le besoin d'une instruction ternaire.
df['C'] = df.apply(lambda row: min([abs(x - y) for x in row['A'] for y in row['B']], default=0), axis=1)
Génial @Filipe +1.
@pygo remarque que cela sera plus lent que la solution de jpp et yatu. :-)
Je veux simplement vous présenter à nouveau le désemboîtement
def unnesting(df, explode):
idx=df.index.repeat(df[explode[0]].str.len())
df1=pd.concat([pd.DataFrame({x:np.concatenate(df[x].values)} )for x in explode],axis=1)
df1.index=idx
return df1.join(df.drop(explode,1),how='left')
FYI
df['Diff']=unnesting(df[['B']],['B']).join(unnesting(df[['A']],['A'])).eval('C=B-A').C.abs().min(level=0)
df.Diff=df.Diff.fillna(0).astype(int)
df
Out[60]:
A B Diff
0 [1, 5, 10] [15, 2] 1
1 [] [] 0
2 [2] [6] 4
3 [1, 2] [] 0