J'ai une base de données contenant une seule colonne d'identifiants et toutes les autres colonnes sont des valeurs numériques pour lesquelles je souhaite calculer des scores z. En voici une sous-section:
ID Age BMI Risk Factor
PT 6 48 19.3 4
PT 8 43 20.9 NaN
PT 2 39 18.1 3
PT 9 41 19.5 NaN
Certaines de mes colonnes contiennent des valeurs de NaN que je ne souhaite pas inclure dans les calculs du z-score. Je compte donc utiliser une solution proposée pour cette question: comment zscore normaliser pandas colonne avec nans?
df['zscore'] = (df.a - df.a.mean())/df.a.std(ddof=0)
Je suis intéressé par l’application de cette solution à toutes mes colonnes, à l’exception de la colonne ID, afin de générer une nouvelle trame de données que je peux enregistrer en tant que fichier Excel à l’aide
df2.to_Excel("Z-Scores.xlsx")
Donc en gros; Comment puis-je calculer les scores z pour chaque colonne (en ignorant les valeurs NaN) et tout insérer dans une nouvelle trame de données?
SIDENOTE: il y a un concept dans pandas appelé "indexation") qui m'intimide parce que je ne le comprends pas bien. Si l'indexation est une partie cruciale de la résolution de ce problème, veuillez rendre muette votre explication de l'indexation .
Construisez une liste à partir des colonnes et supprimez la colonne pour laquelle vous ne voulez pas calculer le score Z pour:
In [66]:
cols = list(df.columns)
cols.remove('ID')
df[cols]
Out[66]:
Age BMI Risk Factor
0 6 48 19.3 4
1 8 43 20.9 NaN
2 2 39 18.1 3
3 9 41 19.5 NaN
In [68]:
# now iterate over the remaining columns and create a new zscore column
for col in cols:
col_zscore = col + '_zscore'
df[col_zscore] = (df[col] - df[col].mean())/df[col].std(ddof=0)
df
Out[68]:
ID Age BMI Risk Factor Age_zscore BMI_zscore Risk_zscore \
0 PT 6 48 19.3 4 -0.093250 1.569614 -0.150946
1 PT 8 43 20.9 NaN 0.652753 0.074744 1.459148
2 PT 2 39 18.1 3 -1.585258 -1.121153 -1.358517
3 PT 9 41 19.5 NaN 1.025755 -0.523205 0.050315
Factor_zscore
0 1
1 NaN
2 -1
3 NaN
Utilisation de la fonction zscore de Scipy :
df = pd.DataFrame(np.random.randint(100, 200, size=(5, 3)), columns=['A', 'B', 'C'])
df
| | A | B | C |
|---:|----:|----:|----:|
| 0 | 163 | 163 | 159 |
| 1 | 120 | 153 | 181 |
| 2 | 130 | 199 | 108 |
| 3 | 108 | 188 | 157 |
| 4 | 109 | 171 | 119 |
from scipy.stats import zscore
df.apply(zscore)
| | A | B | C |
|---:|----------:|----------:|----------:|
| 0 | 1.83447 | -0.708023 | 0.523362 |
| 1 | -0.297482 | -1.30804 | 1.3342 |
| 2 | 0.198321 | 1.45205 | -1.35632 |
| 3 | -0.892446 | 0.792025 | 0.449649 |
| 4 | -0.842866 | -0.228007 | -0.950897 |
Si toutes les colonnes de votre cadre de données ne sont pas numériques, vous pouvez appliquer la fonction Z-score uniquement aux colonnes numériques à l'aide de la commande select_dtypes
une fonction:
# Note that `select_dtypes` returns a data frame. We are selecting only the columns
numeric_cols = df.select_dtypes(include=[np.number]).columns
df[numeric_cols].apply(zscore)
| | A | B | C |
|---:|----------:|----------:|----------:|
| 0 | 1.83447 | -0.708023 | 0.523362 |
| 1 | -0.297482 | -1.30804 | 1.3342 |
| 2 | 0.198321 | 1.45205 | -1.35632 |
| 3 | -0.892446 | 0.792025 | 0.449649 |
| 4 | -0.842866 | -0.228007 | -0.950897 |
Si vous souhaitez calculer le score z pour toutes les colonnes, vous pouvez simplement utiliser les éléments suivants:
df_zscore = (df - df.mean())/df.std()
Voici une autre façon d’obtenir Zscore en utilisant une fonction personnalisée:
In [6]: import pandas as pd; import numpy as np
In [7]: np.random.seed(0) # Fixes the random seed
In [8]: df = pd.DataFrame(np.random.randn(5,3), columns=["randomA", "randomB","randomC"])
In [9]: df # watch output of dataframe
Out[9]:
randomA randomB randomC
0 1.764052 0.400157 0.978738
1 2.240893 1.867558 -0.977278
2 0.950088 -0.151357 -0.103219
3 0.410599 0.144044 1.454274
4 0.761038 0.121675 0.443863
## Create custom function to compute Zscore
In [10]: def z_score(df):
....: df.columns = [x + "_zscore" for x in df.columns.tolist()]
....: return ((df - df.mean())/df.std(ddof=0))
....:
## make sure you filter or select columns of interest before passing dataframe to function
In [11]: z_score(df) # compute Zscore
Out[11]:
randomA_zscore randomB_zscore randomC_zscore
0 0.798350 -0.106335 0.731041
1 1.505002 1.939828 -1.577295
2 -0.407899 -0.875374 -0.545799
3 -1.207392 -0.463464 1.292230
4 -0.688061 -0.494655 0.099824
In [12]: from scipy.stats import zscore
In [13]: df.apply(zscore) # (Credit: Manuel)
Out[13]:
randomA randomB randomC
0 0.798350 -0.106335 0.731041
1 1.505002 1.939828 -1.577295
2 -0.407899 -0.875374 -0.545799
3 -1.207392 -0.463464 1.292230
4 -0.688061 -0.494655 0.099824
La solution presque one-liner:
df2 = (df.ix[:,1:] - df.ix[:,1:].mean()) / df.ix[:,1:].std()
df2['ID'] = df['ID']
pour le score Z, nous pouvons nous en tenir à la documentation au lieu d'utiliser la fonction 'appliquer'
df_zscore = scipy.stats.zscore(cols as array, axis=1)
Lorsque nous avons affaire à des séries chronologiques, le calcul des scores z (ou des anomalies - ce n'est pas la même chose, mais vous pouvez facilement adapter ce code) - est un peu plus compliqué. Par exemple, vous avez 10 ans de données de température mesurées chaque semaine. Pour calculer les scores z pour toute la série chronologique, vous devez connaître les moyennes et les écarts types pour chaque jour de l'année. Alors, commençons:
Supposons que vous ayez un pandas DataFrame. Tout d'abord, vous avez besoin d'un index DateTime. Si vous ne l'avez pas encore, mais heureusement, vous avez une colonne avec des dates, faites-le simplement comme votre index. Pandas va essayer de deviner le format de la date. Le but ici est d’avoir DateTimeIndex. Vous pouvez le vérifier en essayant:
type(df.index)
Si vous n'en avez pas, faisons-le.
df.index = pd.DatetimeIndex(df[datecolumn])
df = df.drop(datecolumn,axis=1)
La prochaine étape consiste à calculer la moyenne et l'écart type pour chaque groupe de jours. Pour cela, nous utilisons la méthode groupby.
mean = pd.groupby(df,by=[df.index.dayofyear]).aggregate(np.nanmean)
std = pd.groupby(df,by=[df.index.dayofyear]).aggregate(np.nanstd)
Enfin, nous parcourons toutes les dates en effectuant le calcul (valeur - moyenne)/stddev; cependant, comme mentionné, pour les séries chronologiques, ce n'est pas si simple.
df2 = df.copy() #keep a copy for future comparisons
for y in np.unique(df.index.year):
for d in np.unique(df.index.dayofyear):
df2[(df.index.year==y) & (df.index.dayofyear==d)] = (df[(df.index.year==y) & (df.index.dayofyear==d)]- mean.ix[d])/std.ix[d]
df2.index.name = 'date' #this is just to look nicer
df2 #this is your z-score dataset.
La logique à l’intérieur des boucles for est la suivante: pour une année donnée, nous devons faire correspondre chaque jour d’une année à sa moyenne et à sa moyenne. Nous l’utilisons pour toutes les années de votre série chronologique.