将分类数据转换为虚拟集

时间:2019-02-13 13:26:53

标签: python pandas scikit-learn dummy-variable

我有这样的数据:-

|--------|---------|
| Col1   | Col2    |
|--------|---------|
| X      | a,b,c   |
|--------|---------|
| Y      | a,b     |
|--------|---------|
| X      | b,d     |
|--------|---------|

我想将这些分类数据转换为虚拟变量。由于我的数据很大,如果我使用的是熊猫的get_dummies(),它会给内存带来错误。我想要这样的结果:-

|------|------|------|------|------|------|
|Col_X |Col_Y |Col2_a|Col2_b|Col2_c|Col2_d|
|------|------|------|------|------|------|
|  1   |  0   |  1   |  1   |  1   |  0   |
|------|------|------|------|------|------|
|  0   | 1    |  1   |  1   |  0   |   0  |
|------|------|------|------|------|------|
|  1   | 0    |  0   |  1   |  0   |   1  |
|------|------|------|------|------|------|

我曾尝试使用this转换Col2,但是由于数据量大而导致出现MemoryError,并且col2中也存在很多可变性。

所以

1)如何将多个分类列转换为虚拟变量?

2)熊猫get_dummy()正在给出内存错误,那么我该如何处理呢?

2 个答案:

答案 0 :(得分:1)

对于您遇到内存问题,我几乎是肯定的,因为str.get_dummies返回的数据类型为np.int64的数组由1和0组成。这与pd.get_dummies的行为完全不同,后者的行为是返回数据类型为uint8的值的数组。

这似乎是known issue。但是,过去一年没有更新,也没有修复。检出source code中的str.get_dummies确实会确认它正在返回np.int64

一个8位整数将占用1个字节的内存,而一个64位整数将占用8个字节。我希望通过找到一种单编码Col2的替代方法来避免内存问题,该方法可以确保输出都是8位整数。

这是我的方法,从您的示例开始:

df = pd.DataFrame({'Col1': ['X', 'Y', 'X'],
                   'Col2': ['a,b,c', 'a,b', 'b,d']})
df

    Col1    Col2
0   X       a,b,c
1   Y       a,b
2   X       b,d
  1. 由于Col1包含简单的,非定界的字符串,因此我们可以轻松地使用pd.get_dummies对它进行一次热编码:
df = pd.get_dummies(df, columns=['Col1'])
df

    Col2    Col1_X  Col1_Y
0   a,b,c        1       0
1   a,b          0       1
2   b,d          1       0

到目前为止很好。

df['Col1_X'].values.dtype
dtype('uint8')
  1. 让我们获取Col2中用逗号分隔的字符串中包含的所有唯一子字符串的列表:
vals = list(df['Col2'].str.split(',').values)
vals = [i for l in vals for i in l]
vals = list(set(vals))
vals.sort()
vals

['a', 'b', 'c', 'd']
  1. 现在,我们可以遍历上面的值列表,并使用str.contains为每个值创建一个新列,例如'a'。如果新行中的每一行在'a'的字符串中实际上具有新列的值,例如Col2,则该行将包含1。创建每个新列时,请确保将其数据类型转换为uint8
col='Col2'
for v in vals:
    n = col + '_' + v
    df[n] = df[col].str.contains(v)
    df[n] = df[n].astype('uint8')

df.drop(col, axis=1, inplace=True)
df

    Col1_X  Col1_Y  Col2_a  Col2_b  Col2_c  Col2_d
0        1       0       1       1       1       0
1        0       1       1       1       0       0
2        1       0       0       1       0       1

这将导致数据框符合您所需的格式。值得庆幸的是,从Col2开始进行一键编码的四个新列中的整数每个仅占用1个字节,而不是每个8个字节。

df['Col2_a'].dtype
dtype('uint8')

如果在偶然的情况下,上述方法不起作用。我的建议是使用str.get_dummies对行块中的Col2进行一次热编码。每次执行块时,都将其数据类型从np.int64转换为uint8,然后转换为transform the chunk to a sparse matrix。您最终可以将所有块连接在一起。

答案 1 :(得分:1)

我也想提供我的解决方案。我还要感谢@ James-dellinger的回答。所以这是我的方法

df = pd.DataFrame({'Col1': ['X', 'Y', 'X'],
               'Col2': ['a,b,c', 'a,b', 'b,d']})
df

  Col1  Col2
0   X   a,b,c
1   Y   a,b
2   X   b,d

我首先拆分Col2值并将其转换为列值。

df= pd.DataFrame(df['Col2'].str.split(',',3).tolist(),columns = ['Col1','Col2','Col3'])

df

   Col1 Col2 Col3
0   a   b    c
1   a   b    None
2   b   d    None

然后我在不提供任何前缀的情况下对此数据帧应用了虚拟创建。

df=pd.get_dummies(df, prefix="")

df

    _a  _b  _b  _d  _c
0   1   0   1   0   1
1   1   0   1   0   0
2   0   1   0   1   0

现在要获得所需的结果,我们可以汇总所有重复的列。

df.groupby(level=0, axis=1).sum()

df

    _a  _b  _c  _d
0   1   1   1   0
1   1   1   0   0
2   0   1   0   1

对于Col1,我们可以使用pd.get_dummies()直接创建伪变量,并将其存储到假设为col1_df的其他数据框中。我们可以使用pd.concat([df,col1_df], axis=1, sort=False)

合并这两列