2
votes

Trouver la plus petite différence possible entre des listes de longueur inégale

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], []]})


0 commentaires

5 Réponses :


0
votes

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


0 commentaires

3
votes

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


5 commentaires

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 :)



2
votes

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]


1 commentaires

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.



2
votes
df['C'] = df.apply(lambda row: min([abs(x - y) for x in row['A'] for y in row['B']], default=0), axis=1)

2 commentaires

Génial @Filipe +1.


@pygo remarque que cela sera plus lent que la solution de jpp et yatu. :-)



1
votes

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


0 commentaires