Python Pandas: ¿cómo convertir un DataFrame con “factores” en una matriz de diseño para la regresión lineal?

Si la memoria me sirve, en R hay un tipo de datos llamado factor que, cuando se utiliza dentro de un DataFrame, puede descomprimirse automáticamente en las columnas necesarias de una matriz de diseño de regresión. Por ejemplo, un factor que contiene los valores de Verdadero / Falso / Tal vez se transformaría en:

1 0 0 0 1 0 or 0 0 1 

con el fin de utilizar el código de regresión de nivel inferior. ¿Hay alguna manera de lograr algo similar usando la biblioteca de pandas? Veo que hay algo de soporte de regresión dentro de Pandas, pero como tengo mis propias rutinas de regresión personalizadas, estoy realmente interesado en la construcción de la matriz de diseño (una matriz o matriz de 2 números) a partir de datos heterogéneos con soporte para mapeo de ida y vuelta entre columnas del objeto numpy y el dataframe de Pandas del que se deriva.

Actualización: Este es un ejemplo de una matriz de datos con datos heterogéneos del tipo que estoy pensando (el ejemplo proviene del manual de Pandas):

 >>> df2 = DataFrame({'a' : ['one', 'one', 'two', 'three', 'two', 'one', 'six'],'b' : ['x', 'y', 'y', 'x', 'y', 'x', 'x'],'c' : np.random.randn(7)}) >>> df2 abc 0 one x 0.000343 1 one y -0.055651 2 two y 0.249194 3 three x -1.486462 4 two y -0.406930 5 one x -0.223973 6 six x -0.189001 >>> 

La columna ‘a’ debe convertirse en 4 columnas de punto flotante (a pesar del significado, solo hay cuatro átomos únicos), la columna ‘b’ se puede convertir en una sola columna de punto flotante y la columna ‘c’ debería Ser una columna final no modificada en la matriz de diseño.

Gracias,

SetJmp

Hay un nuevo módulo llamado patsy que resuelve este problema. El inicio rápido vinculado a continuación resuelve exactamente el problema descrito anteriormente en un par de líneas de código.

Aquí hay un ejemplo de uso:

 import pandas import patsy dataFrame = pandas.io.parsers.read_csv("salary2.txt") #salary2.txt is a re-formatted data set from the textbook #Introductory Econometrics: A Modern Approach #by Jeffrey Wooldridge y,X = patsy.dmatrices("sl ~ 1+sx+rk+yr+dg+yd",dataFrame) #X.design_info provides the meta data behind the X columns print X.design_info 

genera:

 > DesignInfo(['Intercept', > 'sx[T.male]', > 'rk[T.associate]', > 'rk[T.full]', > 'dg[T.masters]', > 'yr', > 'yd'], > term_slices=OrderedDict([(Term([]), slice(0, 1, None)), (Term([EvalFactor('sx')]), slice(1, 2, None)), > (Term([EvalFactor('rk')]), slice(2, 4, None)), > (Term([EvalFactor('dg')]), slice(4, 5, None)), > (Term([EvalFactor('yr')]), slice(5, 6, None)), > (Term([EvalFactor('yd')]), slice(6, 7, None))]), > builder=) 
 import pandas import numpy as np num_rows = 7; df2 = pandas.DataFrame( { 'a' : ['one', 'one', 'two', 'three', 'two', 'one', 'six'], 'b' : ['x', 'y', 'y', 'x', 'y', 'x', 'x'], 'c' : np.random.randn(num_rows) } ) a_attribute_list = ['one', 'two', 'three', 'six']; #Or use list(set(df2['a'].values)), but that doesn't guarantee ordering. b_attribute_list = ['x','y'] a_membership = [ np.reshape(np.array(df2['a'].values == elem).astype(np.float64), (num_rows,1)) for elem in a_attribute_list ] b_membership = [ np.reshape((df2['b'].values == elem).astype(np.float64), (num_rows,1)) for elem in b_attribute_list ] c_column = np.reshape(df2['c'].values, (num_rows,1)) design_matrix_a = np.hstack(tuple(a_membership)) design_matrix_b = np.hstack(tuple(b_membership)) design_matrix = np.hstack(( design_matrix_a, design_matrix_b, c_column )) # Print out the design matrix to see that it's what you want. for row in design_matrix: print row 

Obtengo esta salida:

 [ 1. 0. 0. 0. 1. 0. 0.36444463] [ 1. 0. 0. 0. 0. 1. -0.63610264] [ 0. 1. 0. 0. 0. 1. 1.27876991] [ 0. 0. 1. 0. 1. 0. 0.69048607] [ 0. 1. 0. 0. 0. 1. 0.34243241] [ 1. 0. 0. 0. 1. 0. -1.17370649] [ 0. 0. 0. 1. 1. 0. -0.52271636] 

Por lo tanto, la primera columna es un indicador de las ubicaciones de DataFrame que fueron ‘uno’, la segunda columna es un indicador de las ubicaciones de DataFrame que fueron ‘dos’, y así sucesivamente. Las columnas 4 y 5 son indicadores de las ubicaciones de DataFrame que eran ‘x’ e ‘y’, respectivamente, y la columna final es solo los datos aleatorios.

Pandas 0.13.1 desde el 3 de febrero de 2014 tiene un método:

 >>> pd.Series(['one', 'one', 'two', 'three', 'two', 'one', 'six']).str.get_dummies() one six three two 0 1 0 0 0 1 1 0 0 0 2 0 0 0 1 3 0 0 1 0 4 0 0 0 1 5 1 0 0 0 6 0 1 0 0 

patsy.dmatrices puede en muchos casos funcionar bien. Si solo tiene un vector, una serie de pandas.Series , entonces el siguiente código puede funcionar para producir una matriz de diseño degenerada y sin una columna de intercepción.

 def factor(series): """Convert a pandas.Series to pandas.DataFrame design matrix. Parameters ---------- series : pandas.Series Vector with categorical values Returns ------- pandas.DataFrame Design matrix with ones and zeroes. See Also -------- patsy.dmatrices : Converts categorical columns to numerical Examples -------- >>> import pandas as pd >>> design = factor(pd.Series(['a', 'b', 'a'])) >>> design.ix[0,'[a]'] 1.0 >>> list(design.columns) ['[a]', '[b]'] """ levels = list(set(series)) design_matrix = np.zeros((len(series), len(levels))) for row_index, elem in enumerate(series): design_matrix[row_index, levels.index(elem)] = 1 name = series.name or "" columns = map(lambda level: "%s[%s]" % (name, level), levels) df = pd.DataFrame(design_matrix, index=series.index, columns=columns) return df