3
votes

Comment trier une colonne de tableaux en fonction d'une autre colonne de tableaux dans les pandas?

J'ai un dataframe comme celui-ci:

sort_idx = df1['col1'].apply(np.argsort).values
for rowidxval, (index, row) in enumerate(df1.iterrows()):
    df1['col1'][index] = df1['col1'][index][sort_idx[rowidxval]]
    df1['col2'][index] = df1['col2'][index][sort_idx[rowidxval]]

Je veux trier les valeurs des tableaux de la colonne 2 en fonction des valeurs des tableaux de la colonne 1.

Voici ma solution:

df1= pd.DataFrame({
    'col1': [np.asarray([1,4,3,2]), np.asarray([9,10,7,5]), np.asarray([100,120,10,22])],
    'col2': [np.asarray([0,1,4,5]), np.asarray([100,101,102,103]), np.asarray([10,11,12,13])]
})

df1
                 col1                  col2
0        [1, 4, 3, 2]          [0, 1, 4, 5]
1       [9, 10, 7, 5]  [100, 101, 102, 103]
2  [100, 120, 10, 22]      [10, 11, 12, 13]

Existe-t-il une manière élégante et pythonique de le faire au lieu de trier par force brute les données par ligne? Que faire si je souhaite trier à nouveau plusieurs colonnes en fonction des valeurs de la colonne 1?


0 commentaires

3 Réponses :


5
votes

Les listes en colonnes ne sont jamais recommandées (les dtypes mixtes et les dtypes mutables introduisent des goulots d'étranglement et une réduction des performances dans le code), mais vous pouvez le rendre aussi rapide que possible en utilisant une compréhension de liste:

df['col2'] = [y[x.argsort()] for x, y in zip(df.col1, df.col2)]
df

                 col1                  col2
0        [1, 4, 3, 2]          [0, 5, 4, 1]
1       [9, 10, 7, 5]  [103, 102, 100, 101]
2  [100, 120, 10, 22]      [12, 13, 10, 11]

S'ils sont tous les deux des tableaux, la solution simplifie:

df['col2'] = [np.array(y)[np.argsort(x)] for x, y in zip(df.col1, df.col2)]
df

                 col1                  col2
0        [1, 4, 3, 2]          [0, 5, 4, 1]
1       [9, 10, 7, 5]  [103, 102, 100, 101]
2  [100, 120, 10, 22]      [12, 13, 10, 11]

Pour plus d'informations sur les problèmes liés aux performances, consultez la section "Dtypes mixtes" dans Pour les boucles avec des pandas - Quand dois-je m'en soucier? .


0 commentaires

0
votes

Essayez d'éviter d'utiliser des tableaux NumPy dans des séries. Une telle structure de données ne supportera pas les calculs vectorisés. Puisque dans ce cas tous vos tableaux ont la même taille, vous pouvez facilement les diviser en plusieurs colonnes:

# STEP 1: split NumPy arrays into separate columns
col1 = pd.DataFrame(df1.pop('col1').values.tolist()).add_prefix('col1_')
col2 = pd.DataFrame(df1.pop('col2').values.tolist()).add_prefix('col2_')
df1 = df1.join(pd.concat([col1, col2], axis=1))

# STEP 2: calculate indices for NumPy assignment
x_idx = np.arange(df1.shape[0])[:, None]
y_idx = df1.iloc[:, :4].values.argsort(1)

# STEP 3: assign via iloc
df1.iloc[:, 4:] = df1.iloc[:, 4:].values[x_idx, y_idx]

print(df1)

#    col1_0  col1_1  col1_2  col1_3  col2_0  col2_1  col2_2  col2_3
# 0       1       4       3       2       0       5       4       1
# 1       9      10       7       5     103     102     100     101
# 2     100     120      10      22      12      13      10      11


0 commentaires

3
votes

Utilisation de la boucle for

[[z for _,z in sorted(zip(x,y))] for x, y in zip(df1.col1, df1.col2)]
Out[250]: [[0, 5, 4, 1], [103, 102, 100, 101], [12, 13, 10, 11]]

#df1.col2=[[z for _,z in sorted(zip(x,y))] for x, y in zip(df1.col1, df1.col2)]


0 commentaires