如何从 csv 文件制作稀疏 pandas DataFrame
how to make a sparse pandas DataFrame from a csv file
我有一个相当大的(1.3 GB,未压缩)csv 文件,有 2 个密集列和 1.4 K 个稀疏列,大约 1 M 行。
我需要从中制作一个pandas.DataFrame。
对于小文件我可以简单地做:
df = pd.read_csv('file.csv')
对于我现在拥有的大文件,出现内存错误,显然是由于 DataFrame 大小(由 sys.getsizeof(df)
测试
基于此文档:
https://pandas.pydata.org/pandas-docs/stable/user_guide/sparse.html#migrating
看来我可以制作一个包含密集列和稀疏列的 DataFrame。
但是,我只能从 csv 文件中看到添加单个稀疏列的说明,而不是将它们全部加在一起的说明。
逐一读取 csv 稀疏列并将它们添加到 df 使用:
for colname_i in names_of_sparse_columns:
data = pd.read_csv('file.csv', usecols = [colname_i])
df[colname_i] = pd.arrays.SparseArray(data.values.transpose()[0])
有效,并且 df
保持非常小,如预期的那样,但执行时间长得离谱。
我当然试过了:
pd.read_csv(path_to_input_csv, usecols = names_of_sparse_columns, dtype = "Sparse[float]")
但这会产生此错误:
NotImplementedError: Extension Array: <class 'pandas.core.arrays.sparse.array.SparseArray'> must implement _from_sequence_of_strings in order to be used in parser methods
Any idea how I can do this more efficiently?
我查了几个帖子,但他们似乎都在追求与此略有不同的东西。
编辑 添加一个小例子,以阐明
import numpy as np
import pandas as pd
import sys
# Create an unpivoted sparse dataset
lengths = list(np.random.randint(low = 1, high = 5, size = 10000))
cols = []
for l in lengths:
cols.extend(list(np.random.choice(100, size = l, replace = False)))
rows = np.repeat(np.arange(10000), lengths)
vals = np.repeat(1, sum(lengths))
df_unpivoted = pd.DataFrame({"row" : rows, "col" : cols, "val" : vals})
# Pivot and save to a csv file
df = df_unpivoted.pivot(index = "row", columns = "col", values = "val")
df.to_csv("sparse.csv", index = False)
此文件在我的电脑上占用 1 MB。
相反:
sys.getsizeof(df)
# 8080016
我觉得这好像是 8 MB。
因此,当从稀疏 csv 文件制作 pd.DataFrame
时,大小明显增加了很多(在这种情况下,我从数据框中制作了文件,但它与使用 [=25= 读取 csv 文件相同) ]).
这就是我的观点:我不能使用pd.read_csv()
将整个csv文件加载到内存中。
这里只有8MB,完全没有问题;对于我提到的实际 1.3 GB csv,它的大小如此之大,以至于使我们的机器内存崩溃。
我想这很容易尝试,在上面的模拟中将 10000 替换为 1000000,将 100 替换为 1500。
如果我这样做:
names_of_sparse_columns = df.columns.values
df_sparse = pd.DataFrame()
for colname_i in names_of_sparse_columns:
data = pd.read_csv('sparse.csv', usecols = [colname_i])
df_sparse[colname_i] = pd.arrays.SparseArray(data.values.transpose()[0])
生成的对象小得多:
sys.getsizeof(df_sparse)
# 416700
实际上比文件还要小。
这是我的第二点:对稀疏列进行逐列相加非常慢。
I was looking for advice on how to make df_sparse
from a file like "sparse.csv"
faster / more efficiently.
事实上,当我写这个例子的时候,我注意到:
sys.getsizeof(df_unpivoted)
# 399504
所以也许解决方案是逐行读取 csv 文件并将其取消透视。然而,我需要做的其余处理仍然需要我写出一个旋转的 csv,所以回到第一个问题。
编辑 2 更多信息
我也描述了我需要做的其余处理。
当我可以使用非稀疏数据框时,文件中有一个 ID
列:
df["ID"] = list(np.random.choice(20, df.shape[0]))
我需要总结每个 ID
每个数据列存在多少数据:
df.groupby("ID").count()
不幸的是稀疏数据框不支持这个。
我找到了解决方法,但它非常低效且缓慢。
If anyone can advise on that aspect, too, it would be useful.
我猜会有一种方法可以将 csv 的稀疏部分加载到某种形式的稀疏数组中,并通过 ID
.
进行汇总
也许我的处理方式完全错误,这就是为什么我向广大有能力的听众征求意见的原因。
不应在每次迭代时都读取相同的 file.csv;这行代码:
data = pd.read_csv('file.csv', ...)
应该移到 for 循环之前。
遍历names_of_sparse_columns:
df = pd.read_csv('file.csv', header = 0).copy()
data = pd.read_csv('file.csv', header = 0).copy()
for colname_i in names_of_sparse_columns:
dataFromThisSparseColumn = data[colname_i]
df[colname_i] = np.reshape(dataFromThisSparseColumn, -1)
我完全不知道为什么有人会制作那种格式的 CSV。我只是将它作为块读取并修复块。
# Read in chunks of data, melt it into an dataframe that makes sense
data = [c.melt(id_vars=dense_columns, var_name="Column_label", value_name="Thing").dropna()
for c in pd.read_csv('file.csv', iterator=True, chunksize=100000)]
# Concat the data together
data = pd.concat(data, axis=0)
根据需要更改块大小和值列的名称。如果需要,您也可以分块读取并将这些块转换为稀疏数据帧,但似乎您最好使用融化的数据帧来完成您想要做的事情,IMO。
您也可以随时将其以另一种方式再次分块。根据数据需要更改块数。
with open('out_file.csv', mode='w') as out:
for i, chunk in enumerate(np.array_split(df, 100)):
chunk.iloc[:, 2:] = chunk.iloc[:, 2:].sparse.to_dense()
chunk.to_csv(out, header=i==0)
我有一个相当大的(1.3 GB,未压缩)csv 文件,有 2 个密集列和 1.4 K 个稀疏列,大约 1 M 行。
我需要从中制作一个pandas.DataFrame。
对于小文件我可以简单地做:
df = pd.read_csv('file.csv')
对于我现在拥有的大文件,出现内存错误,显然是由于 DataFrame 大小(由 sys.getsizeof(df)
基于此文档:
https://pandas.pydata.org/pandas-docs/stable/user_guide/sparse.html#migrating
看来我可以制作一个包含密集列和稀疏列的 DataFrame。
但是,我只能从 csv 文件中看到添加单个稀疏列的说明,而不是将它们全部加在一起的说明。
逐一读取 csv 稀疏列并将它们添加到 df 使用:
for colname_i in names_of_sparse_columns:
data = pd.read_csv('file.csv', usecols = [colname_i])
df[colname_i] = pd.arrays.SparseArray(data.values.transpose()[0])
有效,并且 df
保持非常小,如预期的那样,但执行时间长得离谱。
我当然试过了:
pd.read_csv(path_to_input_csv, usecols = names_of_sparse_columns, dtype = "Sparse[float]")
但这会产生此错误:
NotImplementedError: Extension Array: <class 'pandas.core.arrays.sparse.array.SparseArray'> must implement _from_sequence_of_strings in order to be used in parser methods
Any idea how I can do this more efficiently?
我查了几个帖子,但他们似乎都在追求与此略有不同的东西。
编辑 添加一个小例子,以阐明
import numpy as np
import pandas as pd
import sys
# Create an unpivoted sparse dataset
lengths = list(np.random.randint(low = 1, high = 5, size = 10000))
cols = []
for l in lengths:
cols.extend(list(np.random.choice(100, size = l, replace = False)))
rows = np.repeat(np.arange(10000), lengths)
vals = np.repeat(1, sum(lengths))
df_unpivoted = pd.DataFrame({"row" : rows, "col" : cols, "val" : vals})
# Pivot and save to a csv file
df = df_unpivoted.pivot(index = "row", columns = "col", values = "val")
df.to_csv("sparse.csv", index = False)
此文件在我的电脑上占用 1 MB。
相反:
sys.getsizeof(df)
# 8080016
我觉得这好像是 8 MB。
因此,当从稀疏 csv 文件制作 pd.DataFrame
时,大小明显增加了很多(在这种情况下,我从数据框中制作了文件,但它与使用 [=25= 读取 csv 文件相同) ]).
这就是我的观点:我不能使用pd.read_csv()
将整个csv文件加载到内存中。
这里只有8MB,完全没有问题;对于我提到的实际 1.3 GB csv,它的大小如此之大,以至于使我们的机器内存崩溃。
我想这很容易尝试,在上面的模拟中将 10000 替换为 1000000,将 100 替换为 1500。
如果我这样做:
names_of_sparse_columns = df.columns.values
df_sparse = pd.DataFrame()
for colname_i in names_of_sparse_columns:
data = pd.read_csv('sparse.csv', usecols = [colname_i])
df_sparse[colname_i] = pd.arrays.SparseArray(data.values.transpose()[0])
生成的对象小得多:
sys.getsizeof(df_sparse)
# 416700
实际上比文件还要小。
这是我的第二点:对稀疏列进行逐列相加非常慢。
I was looking for advice on how to make
df_sparse
from a file like"sparse.csv"
faster / more efficiently.
事实上,当我写这个例子的时候,我注意到:
sys.getsizeof(df_unpivoted)
# 399504
所以也许解决方案是逐行读取 csv 文件并将其取消透视。然而,我需要做的其余处理仍然需要我写出一个旋转的 csv,所以回到第一个问题。
编辑 2 更多信息
我也描述了我需要做的其余处理。
当我可以使用非稀疏数据框时,文件中有一个 ID
列:
df["ID"] = list(np.random.choice(20, df.shape[0]))
我需要总结每个 ID
每个数据列存在多少数据:
df.groupby("ID").count()
不幸的是稀疏数据框不支持这个。
我找到了解决方法,但它非常低效且缓慢。
If anyone can advise on that aspect, too, it would be useful.
我猜会有一种方法可以将 csv 的稀疏部分加载到某种形式的稀疏数组中,并通过 ID
.
也许我的处理方式完全错误,这就是为什么我向广大有能力的听众征求意见的原因。
不应在每次迭代时都读取相同的 file.csv;这行代码:
data = pd.read_csv('file.csv', ...)
应该移到 for 循环之前。
遍历names_of_sparse_columns:
df = pd.read_csv('file.csv', header = 0).copy()
data = pd.read_csv('file.csv', header = 0).copy()
for colname_i in names_of_sparse_columns:
dataFromThisSparseColumn = data[colname_i]
df[colname_i] = np.reshape(dataFromThisSparseColumn, -1)
我完全不知道为什么有人会制作那种格式的 CSV。我只是将它作为块读取并修复块。
# Read in chunks of data, melt it into an dataframe that makes sense
data = [c.melt(id_vars=dense_columns, var_name="Column_label", value_name="Thing").dropna()
for c in pd.read_csv('file.csv', iterator=True, chunksize=100000)]
# Concat the data together
data = pd.concat(data, axis=0)
根据需要更改块大小和值列的名称。如果需要,您也可以分块读取并将这些块转换为稀疏数据帧,但似乎您最好使用融化的数据帧来完成您想要做的事情,IMO。
您也可以随时将其以另一种方式再次分块。根据数据需要更改块数。
with open('out_file.csv', mode='w') as out:
for i, chunk in enumerate(np.array_split(df, 100)):
chunk.iloc[:, 2:] = chunk.iloc[:, 2:].sparse.to_dense()
chunk.to_csv(out, header=i==0)