Python Pandas: как превратить DataFrame с "факторами" в матрицу дизайна для линейной регрессии?
Если память обслуживает меня, в R есть тип данных, называемый фактором, который при использовании в DataFrame может быть автоматически распакован в необходимые столбцы матрицы проектирования регрессии. Например, фактор, содержащий значения True/False/Maybe, будет преобразован в:
1 0 0
0 1 0
or
0 0 1
с целью использования кода регрессии нижнего уровня. Есть ли способ добиться чего-то подобного с помощью библиотеки pandas? Я вижу, что существует некоторая поддержка регрессии внутри Pandas, но поскольку у меня есть собственные настраиваемые процедуры регрессии, мне действительно интересно построить конструктивную матрицу (массив или матрицу 2d numpy) из гетерогенных данных с поддержкой отображения назад и форт между столбцами объекта numpy и pandas DataFrame, из которого он получен.
Обновление: вот пример матрицы данных с разнородными данными, о которых я думаю (пример приведен в руководстве 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
a b c
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
>>>
Столбец "a" должен быть преобразован в 4 столбца с плавающей запятой (несмотря на то, что имеется только четыре уникальных атома), столбец "b" можно преобразовать в один столбец с плавающей точкой, а "c" 'столбец должен быть немодифицированным окончательным столбцом в матрице дизайна.
Спасибо,
SetJmp
Ответы
Ответ 1
Существует новый модуль под названием patsy, который решает эту проблему. В приведенной ниже ссылке quickstart решается точно проблема, описанная выше, в парах строк кода.
Вот пример использования:
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
генерирует:
> 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=<patsy.build.DesignMatrixBuilder at 0x10f169510>)
Ответ 2
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 what you want.
for row in design_matrix:
print row
Я получаю этот вывод:
[ 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]
Итак, первый столбец является индикатором для местоположений DataFrame, которые были "одним", второй столбец является индикатором местоположений DataFrame, которые были "двумя", и так далее. Столбцы 4 и 5 являются индикаторами местоположений DataFrame, которые были соответственно "x" и "y", а конечный столбец - это только случайные данные.
Ответ 3
Pandas 0.13.1 с 3 февраля 2014 года имеет метод:
>>> 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
Ответ 4
patsy.dmatrices
может во многих случаях работать хорошо. Если у вас просто есть вектор - a pandas.Series
- тогда приведенный ниже код может работать, создавая вырожденную матрицу дизайна и без столбца перехвата.
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
Ответ 5
import pandas as pd
import numpy as np
def get_design_matrix(data_in,columns_index,ref):
columns_index_temp = columns_index.copy( )
design_matrix = pd.DataFrame(np.zeros(shape = [len(data_in),len(columns_index)-1]))
columns_index_temp.remove(ref)
design_matrix.columns = columns_index_temp
for ii in columns_index_temp:
loci = list(map(lambda x:x == ii,data_in))
design_matrix.loc[loci,ii] = 1
return(design_matrix)
get_design_matrix(data_in = ['one','two','three','six','one','two'],
columns_index = ['one','two','three','six'],
ref = 'one')
Out[3]:
two three six
0 0.0 0.0 0.0
1 1.0 0.0 0.0
2 0.0 1.0 0.0
3 0.0 0.0 1.0
4 0.0 0.0 0.0
5 1.0 0.0 0.0