无法设置从 Numpy 数组读取的适当 dtype

Posted

技术标签:

【中文标题】无法设置从 Numpy 数组读取的适当 dtype【英文标题】:Can't set appropriate dtypes reading from a Numpy array 【发布时间】:2015-11-02 22:28:58 【问题描述】:

我想保存数据框的一些属性并给定底层 numpy 数组的一部分,我想重建数据框,就好像我已经获取了数据框的一部分一样。如果对象列的值可以强制转换为浮点数,我想不出任何可行的方法。在真实数据集中,我有数百万个观察值和数百列。

实际用例涉及 Pandas 与 scikit-learn 交互的自定义​​代码。我知道 scikit-learn 的最新版本与内置的 pandas 兼容,但我无法使用此版本,因为 RandomizedSearchCV 对象无法处理大参数网格(这将在未来的版本中修复)。

data = [[2, 4, "Focus"],
        [3, 4, "Fiesta",],
        [1, 4, "300"],
        [7, 3, "Pinto"]]

# This dataframe is exactly as intended
df = pd.DataFrame(data=data)

# Slice a subset of the underlying numpy array
raw_slice = df.values[1:,:]

# Try using the dtype option to force dtypes
df_dtype = pd.DataFrame(data=raw_slice, dtype=df.dtypes)
print "\n Dtype arg doesn't use passed dtypes \n", df_dtype.dtypes

# Try converting objects to numeric after reading into dataframe
df_convert = pd.DataFrame(data=raw_slice).convert_objects(convert_numeric=True)
print "\n Convert objects drops object values that are not numeric \n", df_convert
[Out]
 Converted data does not use passed dtypes 
0    object
1    object
2    object
dtype: object

 Converted data drops object values that are not numeric 
   0  1    2
0  3  4  NaN
1  1  4  300
2  7  3  NaN

编辑: 谢谢@unutbu 的回答,它准确地回答了我的问题。在 0.16.0 之前的 scikit-learn 版本中,gridsearch 对象从 pandas 数据帧中剥离了底层的 numpy 数组。这意味着单个对象列使整个数组成为对象,并且 pandas 方法不能包装在自定义转换器中。

使用@unutbu 的答案的解决方案是使管道的第一步成为自定义“DataFrameTransformer”对象。

class DataFrameTransformer(BaseEstimator, TransformerMixin):
    def __init__(self, X):
        self.columns = list(X.columns)
        self.dtypes = X.dtypes

    def fit(self, X, y=None):
        return self

    def transform(self, X, y=None):
        X = pd.DataFrame(X, columns=self.columns)
        for col, dtype in zip(X, self.dtypes):
            X[col] = X[col].astype(dtype)
        return X

在管道中,只需在构造函数中包含您原来的 df:

pipeline = Pipeline([("df_converter", DataFrameTransformer(X)),
                      ...,
                     ("rf", RandomForestClassifier())])

【问题讨论】:

您是否尝试将raw_slice 保存到磁盘? 请注意,在这种情况下,没有“底层 numpy 数组”。有两个,因为 numpy 数组是同质的。因此,有一个底层 int64 数组和一个对象/字符串数组。当您执行值时,您实际上是将它们连接到一个数组中,并且它必须是对象,因为其中一个组件数组是对象。 我不知道。很好的资料! 【参考方案1】:

如果您尝试将 DataFrame 的一部分保存到磁盘,那么一个强大且 方便的方法是使用pd.HDFStore。请注意,这需要 要安装的 PyTables。

# To save the slice `df.iloc[1:, :]` to disk:
filename = '/tmp/test.h5'
with pd.HDFStore(filename) as store:
    store['mydata'] = df.iloc[1:, :]

# To load the DataFrame from disk:
with pd.get_store(filename) as store:
    newdf2 = store['mydata']
    print(newdf2.dtypes)
    print(newdf2)

产量

0     int64
1     int64
2    object
dtype: object
   0  1       2
0  3  4  Fiesta
1  1  4     300
2  7  3   Pinto

从 NumPy 数组(对象 dtype!)重建子数据帧 和df.dtypes,你可以使用

import pandas as pd
data = [[2, 4, "Focus"],
        [3, 4, "Fiesta",],
        [1, 4, "300"],
        [7, 3, "Pinto"]]

# This dataframe is exactly as intended
df = pd.DataFrame(data=data)

# Slice a subset of the `values` numpy object array
raw_slice = df.values[1:,:]

newdf = pd.DataFrame(data=raw_slice)
for col, dtype in zip(newdf, df.dtypes):
    newdf[col] = newdf[col].astype(dtype)
print(newdf.dtypes)
print(newdf)

产生与上述相同的结果。但是,如果您不保存 raw_slice 到磁盘,那么你可以简单地保留一个 引用 df.iloc[1:, :] 而不是将数据转换为 NumPy 数组 object dtype - 一种相对低效的数据结构(就内存和 性能)。

【讨论】:

以上是关于无法设置从 Numpy 数组读取的适当 dtype的主要内容,如果未能解决你的问题,请参考以下文章

numpy:数组的类型

『Numpy』numpy.dtype内存数据解析方式指导

无法将 pandas.Series 转换为 dtype=np.float64 的 numpy.array

『Numpy』内存分析_numpy.dtype内存数据解析方式指导

从一维 numpy 数组中获取这种矩阵的最有效方法是啥?

使用 pandas 读取带有 numpy 数组的 csv