【问题标题】:pd.get_dummies() slow on large levelspd.get_dummies() 在大级别上很慢
【发布时间】:2017-05-29 21:12:52
【问题描述】:

我不确定这是否已经是最快的方法,或者我这样做效率低。

我想对具有 27k+ 个可能级别的特定分类列进行热编码。该列在 2 个不同的数据集中具有不同的值,因此我在使用 get_dummies() 之前先合并了级别

def hot_encode_column_in_both_datasets(column_name,df,df2,sparse=True):
    col1b = set(df2[column_name].unique())
    col1a = set(df[column_name].unique())
    combined_cats = list(col1a.union(col1b))
    df[column_name] = df[column_name].astype('category', categories=combined_cats)
    df2[column_name] = df2[column_name].astype('category', categories=combined_cats)

    df = pd.get_dummies(df, columns=[column_name],sparse=sparse)
    df2 = pd.get_dummies(df2, columns=[column_name],sparse=sparse)
    try:
        del df[column_name]
        del df2[column_name]
    except:
        pass
    return df,df2

但是,它已经运行了 2 个多小时,并且仍然卡在热编码中。

我在这里做错了吗?还是只是在大型数据集上运行它的本质?

Df 有 6.8m 行和 27 列,Df2 在热编码我想要的列之前有 19990 行和 27 列。

建议,谢谢! :)

【问题讨论】:

  • except: pass 总是错误的。我想你想要if column_name in df:。至于您的其他问题,您为什么不告诉我们哪条线路需要很长时间?
  • @JohnZwinck 感谢您的意见 :) 在这种情况下,我认为这并不重要,如果我错了,请纠正我。
  • @JohnZwinck 正如我提到的,get_dummies() 需要很长时间
  • IMO CountVectorizer 是此任务的最佳选择。如果你能提供小的可重复数据集和所需的数据集,我可以写一个小演示......
  • @MaxU 我很想知道如何在数字数据上使用CountVectorizer

标签: python pandas categorical-data


【解决方案1】:

我简要回顾了get_dummies source code,我认为它可能没有充分利用您的用例的稀疏性。以下方法可能更快,但我没有尝试将其一直扩展到您拥有的 19M 记录:

import numpy as np
import pandas as pd
import scipy.sparse as ssp

np.random.seed(1)
N = 10000

dfa = pd.DataFrame.from_dict({
    'col1': np.random.randint(0, 27000, N)
    , 'col2b': np.random.choice([1, 2, 3], N)
    , 'target': np.random.choice([1, 2, 3], N)
    })

# construct an array of the unique values of the column to be encoded
vals = np.array(dfa.col1.unique())
# extract an array of values to be encoded from the dataframe
col1 = dfa.col1.values
# construct a sparse matrix of the appropriate size and an appropriate,
# memory-efficient dtype
spmtx = ssp.dok_matrix((N, len(vals)), dtype=np.uint8)
# do the encoding. NB: This is only vectorized in one of the two dimensions.
# Finding a way to vectorize the second dimension may yield a large speed up
for idx, val in enumerate(vals):
    spmtx[np.argwhere(col1 == val), idx] = 1

# Construct a SparseDataFrame from the sparse matrix and apply the index
# from the original dataframe and column names.
dfnew = pd.SparseDataFrame(spmtx, index=dfa.index,
                           columns=['col1_' + str(el) for el in vals])
dfnew.fillna(0, inplace=True)

更新

借鉴其他答案herehere 的见解,我能够在两个维度上对解决方案进行矢量化。在我有限的测试中,我注意到构建 SparseDataFrame 似乎将执行时间增加了几倍。因此,如果您不需要返回类似 DataFrame 的对象,则可以节省大量时间。此解决方案还处理您需要将 2+ DataFrames 编码为具有相等列数的二维数组的情况。

import numpy as np
import pandas as pd
import scipy.sparse as ssp

np.random.seed(1)
N1 = 10000
N2 = 100000

dfa = pd.DataFrame.from_dict({
    'col1': np.random.randint(0, 27000, N1)
    , 'col2a': np.random.choice([1, 2, 3], N1)
    , 'target': np.random.choice([1, 2, 3], N1)
    })

dfb = pd.DataFrame.from_dict({
    'col1': np.random.randint(0, 27000, N2)
    , 'col2b': np.random.choice(['foo', 'bar', 'baz'], N2)
    , 'target': np.random.choice([1, 2, 3], N2)
    })

# construct an array of the unique values of the column to be encoded
# taking the union of the values from both dataframes.
valsa = set(dfa.col1.unique())
valsb = set(dfb.col1.unique())
vals = np.array(list(valsa.union(valsb)), dtype=np.uint16)


def sparse_ohe(df, col, vals):
    """One-hot encoder using a sparse ndarray."""
    colaray = df[col].values
    # construct a sparse matrix of the appropriate size and an appropriate,
    # memory-efficient dtype
    spmtx = ssp.dok_matrix((df.shape[0], vals.shape[0]), dtype=np.uint8)
    # do the encoding
    spmtx[np.where(colaray.reshape(-1, 1) == vals.reshape(1, -1))] = 1

    # Construct a SparseDataFrame from the sparse matrix
    dfnew = pd.SparseDataFrame(spmtx, dtype=np.uint8, index=df.index,
                               columns=[col + '_' + str(el) for el in vals])
    dfnew.fillna(0, inplace=True)
    return dfnew

dfanew = sparse_ohe(dfa, 'col1', vals)
dfbnew = sparse_ohe(dfb, 'col1', vals)

【讨论】:

  • 您好,谢谢您的回答! :) 这将如何处理第二个数据框中的类别?
  • 您好! :) 如果我理解正确,这只会将当前列作为稀疏数据帧返回,而不是将其组合到原始数据帧中,对吗?此外,我得到一个 ValueError:在尝试时无法将当前的 fill_value nan 强制转换为 uint8 dtype
  • 嗯,我无法重现 ValueError。我使用的是最近才发布的 pandas 0.20.1。如果您需要重新组装一个完整的数据框,其中包括原始的所有列,减去 one-hot-encoded 列,那么您可以在末尾添加此语句:dfa = pd.concat([dfanew, dfa.drop('col1', axis=1)], axis=1)
  • 如果 ValueError 仍然存在,我怀疑从 dtype=np.uint8 调用中删除 dtype=np.uint8 参数将解决它。这个论点不是绝对必要的。
  • np.nan 是一个 np.float 对象。
猜你喜欢
  • 2015-12-03
  • 1970-01-01
  • 1970-01-01
  • 2011-04-13
  • 2013-04-27
  • 1970-01-01
  • 1970-01-01
  • 2016-05-10
  • 1970-01-01
相关资源
最近更新 更多