patsy 中的单热编码
One-hot encoding in patsy
对于回归,我通常使用 sklearn 的 OneHotEncoder
.
对分类变量进行编码
我现在正在探索使用 patsy,但它似乎不提供 One-hot 编码:http://patsy.readthedocs.io/en/latest/categorical-coding.html
是否可以使用 patsy 指定 One-hot 编码?
您提供的link中的第一个示例确实是one-hot编码:
In [3]: data
Out[3]: {'a': ['a1', 'a2', 'a3', 'a1', 'a2', 'a3']}
In [4]: dmatrix("a", data)
Out[4]:
DesignMatrix with shape (6, 3)
Intercept a[T.a2] a[T.a3]
1 0 0
1 1 0
1 0 1
1 0 0
1 1 0
1 0 1
Terms:
'Intercept' (column 0)
'a' (columns 1:3)
请注意,要对具有 3 个级别的分类变量进行编码,您只需要 2 个二进制变量。本文档中的编码将 a1
视为基础,并为 a2
和 a3
添加两个二进制变量。如果a2
和a3
都为0,则表示值为a1
。
在 sklearn 的 OneHotEncoder
或 pandas 的 pd.get_dummies()
中,它们 return 一个矩阵,其列数与分类变量的水平相同,但包含一个额外的列,因为您可以用其他列中的值表示其中一列的值。
这里有两件事可能会有所帮助:(1) 默认情况下 patsy 包含一个截距(每个公式的开头都有一个不可见的 1 +
),以及 (2) 在编写分类代码时值,patsy 自动选择一种编码策略,避免创建过度参数化模型。
如果结合截距+全秩one-hot编码,那么你会得到一个过度参数化的模型。因此 patsy 切换到处理编码(= 基本上从您正在考虑的单热编码中删除一列)。这避免了在编码列和截距列之间产生线性相关性。
避免这种情况的一个简单方法是删除拦截——这样 patsy 就不会担心线性相关性,并且会使用您期望的那种单热编码:y ~ -1 + a
(-1
取消不可见的 1
以删除截距)。
或者,如果您真的想要一个过度参数化的模型,那么如果您在链接到的文档页面上进一步向下滚动,它会告诉您如何定义任意自定义编码方案。
import numpy as np
from patsy import ContrastMatrix
class FullRankOneHot(object):
def __init__(self, reference=0):
self.reference = reference
# Called to generate a full-rank encoding
def code_with_intercept(self, levels):
return ContrastMatrix(np.eye(len(levels)),
["[My.%s]" % (level,) for level in levels])
# Called to generate a non-full-rank encoding. But we don't care,
# we do what we want, and return a full-rank encoding anyway.
# Take that, patsy.
def code_without_intercept(self, levels):
return self.code_with_intercept(levels)
然后你可以像这样使用它:y ~ 1 + C(a, FullRankOneHot)
。
对于回归,我通常使用 sklearn 的 OneHotEncoder
.
我现在正在探索使用 patsy,但它似乎不提供 One-hot 编码:http://patsy.readthedocs.io/en/latest/categorical-coding.html
是否可以使用 patsy 指定 One-hot 编码?
您提供的link中的第一个示例确实是one-hot编码:
In [3]: data
Out[3]: {'a': ['a1', 'a2', 'a3', 'a1', 'a2', 'a3']}
In [4]: dmatrix("a", data)
Out[4]:
DesignMatrix with shape (6, 3)
Intercept a[T.a2] a[T.a3]
1 0 0
1 1 0
1 0 1
1 0 0
1 1 0
1 0 1
Terms:
'Intercept' (column 0)
'a' (columns 1:3)
请注意,要对具有 3 个级别的分类变量进行编码,您只需要 2 个二进制变量。本文档中的编码将 a1
视为基础,并为 a2
和 a3
添加两个二进制变量。如果a2
和a3
都为0,则表示值为a1
。
在 sklearn 的 OneHotEncoder
或 pandas 的 pd.get_dummies()
中,它们 return 一个矩阵,其列数与分类变量的水平相同,但包含一个额外的列,因为您可以用其他列中的值表示其中一列的值。
这里有两件事可能会有所帮助:(1) 默认情况下 patsy 包含一个截距(每个公式的开头都有一个不可见的 1 +
),以及 (2) 在编写分类代码时值,patsy 自动选择一种编码策略,避免创建过度参数化模型。
如果结合截距+全秩one-hot编码,那么你会得到一个过度参数化的模型。因此 patsy 切换到处理编码(= 基本上从您正在考虑的单热编码中删除一列)。这避免了在编码列和截距列之间产生线性相关性。
避免这种情况的一个简单方法是删除拦截——这样 patsy 就不会担心线性相关性,并且会使用您期望的那种单热编码:y ~ -1 + a
(-1
取消不可见的 1
以删除截距)。
或者,如果您真的想要一个过度参数化的模型,那么如果您在链接到的文档页面上进一步向下滚动,它会告诉您如何定义任意自定义编码方案。
import numpy as np
from patsy import ContrastMatrix
class FullRankOneHot(object):
def __init__(self, reference=0):
self.reference = reference
# Called to generate a full-rank encoding
def code_with_intercept(self, levels):
return ContrastMatrix(np.eye(len(levels)),
["[My.%s]" % (level,) for level in levels])
# Called to generate a non-full-rank encoding. But we don't care,
# we do what we want, and return a full-rank encoding anyway.
# Take that, patsy.
def code_without_intercept(self, levels):
return self.code_with_intercept(levels)
然后你可以像这样使用它:y ~ 1 + C(a, FullRankOneHot)
。