Pandas – Calcular la puntuación z para todas las columnas

Tengo un dataframe que contiene una sola columna de ID y todas las demás columnas son valores numéricos para los que quiero calcular las puntuaciones z. Aquí hay una subsección de esto:

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 

Algunas de mis columnas contienen valores de NaN que no deseo incluir en los cálculos de puntuación z, así que pretendo usar una solución ofrecida a esta pregunta: ¿cómo zscore normalizar la columna de pandas con nans?

 df['zscore'] = (df.a - df.a.mean())/df.a.std(ddof=0) 

Estoy interesado en aplicar esta solución a todas mis columnas, excepto la columna ID, para producir un nuevo dataframe que pueda guardar como un archivo de Excel usando

 df2.to_excel("Z-Scores.xlsx") 

Así que básicamente; ¿Cómo puedo calcular las puntuaciones z para cada columna (ignorando los valores de NaN) e introducir todo en un nuevo dataframe?

SIDENOTE: hay un concepto en pandas llamado “indexación” que me intimida porque no lo entiendo bien. Si la indexación es una parte crucial de la solución de este problema, no dude en explicar su explicación de la indexación.

Cree una lista de las columnas y elimine la columna para la que no desea calcular la puntuación Z:

 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 

Usando la función 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 no todas las columnas de su dataframe son numéricas, entonces puede aplicar la función de puntuación Z solo a las columnas numéricas que utilizan la función select_dtypes :

 # 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 desea calcular la puntuación z para todas las columnas, puede utilizar lo siguiente:

 df_zscore = (df - df.mean())/df.std() 

La solución casi de una sola línea:

 df2 = (df.ix[:,1:] - df.ix[:,1:].mean()) / df.ix[:,1:].std() df2['ID'] = df['ID'] 

Aquí hay otra forma de obtener Zscore usando la función personalizada:

 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 

Resultado reproducido utilizando scipy.stats zscore

 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 

para la puntuación Z, podemos mantener la documentación en lugar de usar la función ‘aplicar’

 df_zscore = scipy.stats.zscore(cols as array, axis=1) 

Cuando estamos tratando con series de tiempo, el cálculo de las puntuaciones z (o anomalías, no es lo mismo, pero puede adaptar este código fácilmente) es un poco más complicado. Por ejemplo, usted tiene 10 años de datos de temperatura medidos semanalmente. Para calcular las puntuaciones z para toda la serie de tiempo, debe conocer los promedios y las desviaciones estándar para cada día del año. Entonces empecemos:

Supongamos que tienes un DataFrame pandas. En primer lugar, necesita un índice DateTime. Si aún no lo tiene, pero afortunadamente tiene una columna con fechas, simplemente cámbiela como su índice. Las pandas intentarán adivinar el formato de la fecha. El objective aquí es tener DateTimeIndex. Puedes comprobarlo probando:

 type(df.index) 

Si no tienes uno, hagámoslo.

 df.index = pd.DatetimeIndex(df[datecolumn]) df = df.drop(datecolumn,axis=1) 

El siguiente paso es calcular la media y la desviación estándar para cada grupo de días. Para ello, utilizamos el método groupby.

 mean = pd.groupby(df,by=[df.index.dayofyear]).aggregate(np.nanmean) std = pd.groupby(df,by=[df.index.dayofyear]).aggregate(np.nanstd) 

Finalmente, repasamos todas las fechas, realizando el cálculo (value – mean) / stddev; sin embargo, como se mencionó, para series de tiempo esto no es tan sencillo.

 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 lógica dentro de los bucles for es: para un año dado, tenemos que hacer coincidir cada día de año con su media y estándar. Ejecutamos esto durante todos los años en sus series de tiempo.