为什么在Pandas身上int转换比float慢得多?

2024-09-29 21:58:22 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个4Gb的CSV文件,其中包含严格的整数数据,我想读入pandas DataFrame。本机read_csv消耗所有RAM(64Gb),并因MemoryError而失败。对于显式dtype,它只需要花费很长时间(尝试了int和float类型)。在

所以,我写了我自己的读者:

def read_csv(fname):
    import csv
    reader = csv.reader(open(fname))
    names = reader.next()[1:]  # first row
    dftype = np.float32
    df = pd.DataFrame(0, dtype=dftype, columns=names, index=names)
    for row in reader:
        tag = row[0]
        df.loc[tag] = np.array(row[1:], dtype=dftype)
    return df

问题:如果dftype为np.int32(每行约20秒),所以我最终使用np.浮动64和return df.astype(np.int32)(~4分钟)。我还尝试了Python转换([int/float(v)for v in row[1:]]),结果相同。在

为什么会这样?在

UPD:我在python2.7和3.5上也有相同的行为


Tags: csvindataframedfforreadnamesnp
2条回答

我建议您对此使用numpy数组,例如:

def read_csv(fname):
    import csv
    reader = csv.reader(open(fname))
    names = reader.next()[1:]  # first row
    n = len(names)
    data = np.empty((n, n), np.int32)
    tag_map = {name:i for i, name in enumerate(names)}
    for row in reader:
        tag = row[0]
        data[tag_map[tag], :] = row[1:]
    return names, data

我不知道为什么int32float32慢,但是DataFrame按列存储数据,按df.loc[tag] = ...设置每列的元素很慢。在

如果您需要访问标签,可以使用xarray

^{pr2}$

更新:我的笔记本有16GB的RAM,所以我将用4倍(64GB/16GB=4)的DF测试它:

设置:

In [1]: df = pd.DataFrame(np.random.randint(0, 10*6, (12000, 47395)), dtype=np.int32)

In [2]: df.shape
Out[2]: (12000, 47395)

In [3]: %timeit -n 1 -r 1 df.to_csv('c:/tmp/big.csv', chunksize=1000)
1 loop, best of 1: 5min 34s per loop

我们也将此数据框保存为羽毛格式:

^{pr2}$

然后读回来:

In [10]: %timeit -n 1 -r 1 df = feather.read_dataframe('c:/tmp/big.feather')
1 loop, best of 1: 17.4 s per loop  # reading is reasonably fast as well

从CSV文件中分块读取要慢得多,但仍然不能给我MemoryError

In [2]: %%timeit -n 1 -r 1
   ...: df = pd.DataFrame()
   ...: for chunk in pd.read_csv('c:/tmp/big.csv', index_col=0, chunksize=1000):
   ...:     df = pd.concat([df, chunk])
   ...:     print(df.shape)
   ...: print(df.dtypes.unique())
   ...:
(1000, 47395)
(2000, 47395)
(3000, 47395)
(4000, 47395)
(5000, 47395)
(6000, 47395)
(7000, 47395)
(8000, 47395)
(9000, 47395)
(10000, 47395)
(11000, 47395)
(12000, 47395)
[dtype('int64')]
1 loop, best of 1: 9min 25s per loop

现在让我们显式地指定dtype=np.int32

In [1]: %%timeit -n 1 -r 1
   ...: df = pd.DataFrame()
   ...: for chunk in pd.read_csv('c:/tmp/big.csv', index_col=0, chunksize=1000, dtype=np.int32):
   ...:     df = pd.concat([df, chunk])
   ...:     print(df.shape)
   ...: print(df.dtypes.unique())
   ...:
(1000, 47395)
(2000, 47395)
(3000, 47395)
(4000, 47395)
(5000, 47395)
(6000, 47395)
(7000, 47395)
(8000, 47395)
(9000, 47395)
(10000, 47395)
(11000, 47395)
(12000, 47395)
[dtype('int32')]
1 loop, best of 1: 10min 38s per loop

测试HDF存储:

In [10]: %timeit -n 1 -r 1 df.to_hdf('c:/tmp/big.h5', 'test')
1 loop, best of 1: 22.5 s per loop

In [11]: del df

In [12]: %timeit -n 1 -r 1 df = pd.read_hdf('c:/tmp/big.h5', 'test')
1 loop, best of 1: 1.04 s per loop

结论:

如果你有机会改变你的存储文件格式-无论如何不要使用CSV文件-使用HDF5(.h5)或羽毛格式。。。在

旧答案:

我只需使用原生熊猫read_csv()方法:

chunksize = 10**6
reader = pd.read_csv(filename, index_col=0, chunksize=chunksize)
df = pd.concat([chunk for chunk in reader], ignore_indexes=True)

根据您的代码:

tag = row[0]

df.loc[tag] = np.array(row[1:], dtype=dftype)

看起来您希望使用CSV文件中的第一列作为索引,因此:index_col=0

相关问题 更多 >

    热门问题