J'ai un cadre de données de pandas Je voudrais voir la diagonale à 0
import numpy
import pandas
df = pandas.DataFrame(numpy.random.Rand(5,5))
df
Out[6]:
0 1 2 3 4
0 0.536596 0.674319 0.032815 0.908086 0.215334
1 0.735022 0.954506 0.889162 0.711610 0.415118
2 0.119985 0.979056 0.901891 0.687829 0.947549
3 0.186921 0.899178 0.296294 0.521104 0.638924
4 0.354053 0.060022 0.275224 0.635054 0.075738
5 rows × 5 columns
maintenant je veux mettre la diagonale à 0:
for i in range(len(df.index)):
for j in range(len(df.columns)):
if i==j:
df.loc[i,j] = 0
df
Out[9]:
0 1 2 3 4
0 0.000000 0.674319 0.032815 0.908086 0.215334
1 0.735022 0.000000 0.889162 0.711610 0.415118
2 0.119985 0.979056 0.000000 0.687829 0.947549
3 0.186921 0.899178 0.296294 0.000000 0.638924
4 0.354053 0.060022 0.275224 0.635054 0.000000
5 rows × 5 columns
mais il doit y avoir un moyen plus pythonique que ça!?
In [21]: df.values[[np.arange(df.shape[0])]*2] = 0
In [22]: df
Out[22]:
0 1 2 3 4
0 0.000000 0.931374 0.604412 0.863842 0.280339
1 0.531528 0.000000 0.641094 0.204686 0.997020
2 0.137725 0.037867 0.000000 0.983432 0.458053
3 0.594542 0.943542 0.826738 0.000000 0.753240
4 0.357736 0.689262 0.014773 0.446046 0.000000
Notez que cela ne fonctionnera que si df
a le même nombre de lignes que de colonnes. Une autre façon de travailler avec des formes arbitraires consiste à utiliser np.fill_diagonal :
In [36]: np.fill_diagonal(df.values, 0)
Les deux approches de la réponse de unutbu / supposent que les étiquettes ne sont pas pertinentes (elles agissent sur les valeurs sous-jacentes).
Le code OP fonctionne avec .loc
et est donc basé sur les étiquettes (c.-à-d. Mettez un 0 sur les cellules rangée-colonne avec les mêmes étiquettes, plutôt que sur les cellules situées sur la diagonale - certes, cela n’a pas ne sont que des positions).
Ayant besoin d'un remplissage en diagonale "basé sur une étiquette" (en utilisant une variable DataFrame
décrivant une matrice de adjacence incomplète), l'approche la plus simple que j'ai pu trouver était la suivante:
def pd_fill_diagonal(df, value):
idces = df.index.intersection(df.columns)
stacked = df.stack(dropna=False)
stacked.update(pd.Series(value,
index=pd.MultiIndex.from_arrays([idces,
idces])))
df.loc[:, :] = stacked.unstack()
Cette solution est vectorisée et très rapide et à moins que l’autre solution suggérée ne fonctionne pour n’importe quel nom de colonne et quelle que soit la taille de la matrice df.
def pd_fill_diagonal(df_matrix, value=0):
mat = df_matrix.values
n = mat.shape[0]
mat[range(n), range(n)] = value
return pd.DataFrame(mat)
Performance sur Dataframe de 507 colonnes et lignes
% timeit pd_fill_diagonal(df, 0)
1000 boucles, le meilleur des 3: 145 µs par boucle
Utiliser np.fill_diagonal(df.values, 1)
est la solution la plus simple, mais vous devez vous assurer que vos colonnes ont toutes le même type de données. J'avais un mélange de np.float64 et de python floats et cela affecterait uniquement les valeurs numpy. pour réparer vous devez tout jeter à numpy.
Voici un hack qui a fonctionné pour moi:
def set_diag(self, values):
n = min(len(self.index), len(self.columns))
self.values[[np.arange(n)] * 2] = values
pd.DataFrame.set_diag = set_diag
x = pd.DataFrame(np.random.randn(10, 5))
x.set_diag(0)