J'ai la trame de données pandas suivante df:

index        A    B    C
    1        1    2    3
    2        9    5    4
    3        7    12   8
    ...      ...  ...  ...

Je veux que la valeur maximale de chaque ligne reste inchangée et que toutes les autres valeurs deviennent -1. La sortie ressemblerait donc à ceci:

index        A    B    C
    1       -1   -1    3
    2        9   -1   -1
    3       -1    12  -1
    ...      ...  ...  ...

En utilisant df.max(axis = 1), j'obtiens un pandas Series avec les valeurs maximales par ligne. Cependant, je ne sais pas comment utiliser ces maximums de manière optimale pour créer le résultat dont j'ai besoin. Je recherche une implémentation vectorisée et rapide.

6
S Leon 7 mars 2016 à 00:27

3 réponses

Meilleure réponse

Pensez à utiliser where:

>>> df.where(df.eq(df.max(1), 0), -1)
       A   B  C
index          
1     -1  -1  3
2      9  -1 -1
3     -1  12 -1

Ici df.eq(df.max(1), 0) est un DataFrame booléen marquant les maximums de ligne; Les vraies valeurs (les maximales) restent inchangées tandis que les fausses valeurs deviennent -1. Vous pouvez également utiliser une série ou un autre DataFrame au lieu d'un scalaire si vous le souhaitez.

L'opération peut également être effectuée sur place (en passant inplace=True).

5
Alex Riley 6 mars 2016 à 22:45

Vous pouvez créer un booléen mask en comparant en { {X1}} avec {{X2} } par lignes, puis appliquez le mask inversé:

print df
       A   B  C
index          
1      1   2  3
2      9   5  4
3      7  12  8

print df.max(axis=1)
index
1     3
2     9
3    12
dtype: int64

mask = df.eq(df.max(axis=1), axis=0)
print mask
           A      B      C
index                     
1      False  False   True
2       True  False  False
3      False   True  False

df[~mask] = -1
print df
       A   B  C
index          
1     -1  -1  3
2      9  -1 -1
3     -1  12 -1

Tous ensemble:

df[~df.eq(df.max(axis=1), axis=0)] = -1
print df
       A   B  C
index          
1     -1  -1  3
2      9  -1 -1
3     -1  12 -1
2
jezrael 6 mars 2016 à 21:55

Créez une nouvelle trame de données de la même taille de df composée de -1 pour chaque valeur. Ensuite, utilisez enumerate pour obtenir la première valeur maximale dans une ligne donnée, en utilisant l'obtention / la définition d'un scalaire (iat).

df2 = pd.DataFrame(-np.ones(df.shape), columns=df.columns, index=df.index)

for row, col in enumerate(np.argmax(df.values, axis=1)):
    df2.iat[row, col] = df.iat[row, col]

>>> df2
   0   1  2
0 -1  -1  3
1  9  -1 -1
2 -1  12 -1

Horaires

df = pd.DataFrame(np.random.randn(10000, 10000))

%%timeit
df2 = pd.DataFrame(-np.ones(df.shape))
for row, col in enumerate(np.argmax(df.values, axis=1)):
    df2.iat[row, col] = df.iat[row, col]
1 loops, best of 3: 1.19 s per loop

%timeit df.where(df.eq(df.max(1), 0), -1)
1 loops, best of 3: 6.27 s per loop

# Using inplace=True
%timeit df.where(df.eq(df.max(1), 0), -1, inplace=True)
1 loops, best of 3: 5.58 s per loop

%timeit df[~df.eq(df.max(axis=1), axis=0)] = -1
1 loops, best of 3: 5.65 s per loop
1
Alexander 6 mars 2016 à 23:46