我有一堆csv数据集,每个数据集大小约为10Gb。我想从他们的列生成直方图。但似乎在numpy中执行此操作的唯一方法是首先将整个列加载到numpy数组中,然后在该数组上调用numpy.histogram
。这会消耗不必要的内存量。
numpy支持在线分组吗?我希望能够在读取它们的同时逐行迭代我的csv。这样,任何时候最多只有一行存储在内存中。
自己也不难,但想知道是否有人已经发明了这个轮子。
答案 0 :(得分:12)
正如你所说,推出自己的并不难。您需要自己设置垃圾箱,并在迭代文件时重复使用它们。以下应该是一个不错的起点:
import numpy as np
datamin = -5
datamax = 5
numbins = 20
mybins = np.linspace(datamin, datamax, numbins)
myhist = np.zeros(numbins-1, dtype='int32')
for i in range(100):
d = np.random.randn(1000,1)
htemp, jnk = np.histogram(d, mybins)
myhist += htemp
我猜这种大文件的性能会出现问题,每行调用直方图的开销可能太慢了。 @doug's suggestion生成器似乎是解决该问题的好方法。
答案 1 :(得分:6)
这是一种直接存储您的值的方法:
import numpy as NP
column_of_values = NP.random.randint(10, 99, 10)
# set the bin values:
bins = NP.array([0.0, 20.0, 50.0, 75.0])
binned_values = NP.digitize(column_of_values, bins)
'binned_values'是一个索引数组,包含column_of_values中每个值所属的bin的索引。
'bincount'会给你(显然)bin计数:
NP.bincount(binned_values)
考虑到数据集的大小,使用Numpy的'loadtxt'构建生成器可能很有用:
data_array = NP.loadtxt(data_file.txt, delimiter=",")
def fnx() :
for i in range(0, data_array.shape[1]) :
yield dx[:,i]
答案 2 :(得分:4)
使用Fenwick树进行分区 (非常大的数据集;需要百分位数边界)
我发布了同一个问题的第二个答案,因为这种方法非常不同,并解决了不同的问题。
如果你有一个非常大的数据集(数十亿个样本),并且你不知道你的bin边界应该是什么?例如,您可能希望将事物分成四分位数或十分位数。
对于小型数据集,答案很简单:将数据加载到数组中,然后排序,然后通过跳转到索引通过数组的百分比来读取任何给定百分位数的值。
对于保存数组的内存大小不实用的大型数据集(更不用说排序时间)......然后考虑使用Fenwick树,即“二进制索引树”。
我认为这些仅适用于正整数数据,因此您至少需要了解数据集,以便在将数据列入Fenwick树之前对数据进行移位(并可能缩放)。
我用这个来找到1000亿样本数据集的中位数,在合理的时间和非常舒适的内存限制。 (考虑使用生成器来打开和读取文件,根据我的其他答案;这仍然有用。)
有关芬威克树的更多信息:
答案 3 :(得分:2)
使用生成器分区(大数据集;固定宽度分档;浮动数据)
如果您提前知道所需垃圾箱的宽度 - 即使有数百或数千个桶 - 那么我认为滚动您自己的解决方案会很快(写入和运行)。这里有一些Python假设你有一个迭代器,它为你提供文件中的下一个值:
from math import floor
binwidth = 20
counts = dict()
filename = "mydata.csv"
for val in next_value_from_file(filename):
binname = int(floor(val/binwidth)*binwidth)
if binname not in counts:
counts[binname] = 0
counts[binname] += 1
print counts
值可以是浮点数,但这假设您使用整数binwidth;如果你想使用某个浮点值的binwidth,你可能需要稍微调整一下。
对于next_value_from_file()
,如前所述,您可能希望使用 iter ()方法编写自定义生成器或对象,以便有效地执行此操作。这种发生器的伪代码是这样的:
def next_value_from_file(filename):
f = open(filename)
for line in f:
# parse out from the line the value or values you need
val = parse_the_value_from_the_line(line)
yield val
如果给定的行有多个值,那么make parse_the_value_from_the_line()
要么返回一个列表,要么本身就是一个生成器,并使用这个伪代码:
def next_value_from_file(filename):
f = open(filename)
for line in f:
for val in parse_the_values_from_the_line(line):
yield val