1

我有一个非常大的数据 .dat 文件,我正在加载到一个列表或 numpy 数组中。该文件包含值列,最后一列是该值应加载到数组中的次数的乘数。因此,在加载它时,需要运行第二个子循环以考虑到这一点。这样做是为了使总文件大小更小,因为在某些情况下它们会超过 1Gb。

我使用 numpy 预分配零数组与附加到简单列表进行了一些测试,发现附加更快。虽然,在许多帖子中,情况不应该如此。瓶颈在哪里?

    if True:
            startTime1 = time.clock()
            ## Numpy way to load up the data
            dataAry = np.zeros(TotalSamples)
            multiColAry = np.loadtxt(filename,skiprows=2,                           usecols=(columNum,lastColLoc))
            latestLoc = 0               
            for i in range(0,multiColAry.shape[0]):
                curData = multiColAry[i][0]
                timesBeenHere = int(multiColAry[i][2])
                for j in range(0,timesBeenHere):
                    dataAry[latestLoc] = curData                        
                    latestLoc+=1
            endTime1 = time.clock()
            totalTime = (endTime1-startTime1) # in seconds
            totalTimeString = timeString(totalTime)

        if True:
            #Old string parsing directly version
            startTime1 = time.clock()
            totalAccepted = 0
            f = open(filename,'r')
            dataAry = []
            line = 'asdf'
            while line!='':
                line = f.readline()
                if line!='':
                    dataLineCols = line.split()
                    dataValue = float(dataLineCols[columNum])
                    timesBeenHere = float(dataLineCols[-1])
                    for j in range(0,int(timesBeenHere)):
                        totalAccepted+=1
                        dataAry.append(dataValue)

            f.close()
            endTime1 = time.clock()
            totalTime = (endTime1-startTime1) # in seconds
            totalTimeString = timeString(totalTime)

感谢您的任何意见/建议。

4

2 回答 2

3

在分配一个大的零数组时,您需要清除大量内存,因此如果您使用 np.zeros 分配一个足够大的数组,它可能会开始分页,并且肯定会单独在该调用中清除您的处理器缓存。使用参数分配数组ndarray(shape=(TotalSamples))不会初始化它。

其次,第一个版本的代码会跟踪第二个版本动态丢弃的数据。输入文件显然是一个数字文本表,第一个实现读取列 0 和 2,而第二个实现读取列 columnNum 和 -1。这表明只要 multiColAry 存在,第一个版本就至少保留一个列,而第二个版本在移动到下一行时将其丢弃。您可以使用loadtxt(filename, usecols=(0,2)).

顺便说一句,您知道文件是可迭代的吗?您可以将使用和空字符串测试的棘手组合f.readline()替换为for line in f:空时字符串为假,因此您无需与空字符串进行比较)。

此外,在使用 numpy 时,最好不要像for j in range(0,timesBeenHere):在 Python 中那样编写内部循环。可以使用dataAry[latestLoc:latestLoc+timesBeenHere].fill(curData)或对其进行重组dataAry.append(multiEntries)。创建 multiEntries 本身就是一章,具有诸如np.ones(timesBeenHere)*dataValue(传统但如果不适合缓存则成本高昂)或np.linspace(dataValue,dataValue,timesBeenHere). 另请参阅ndarray.put()

...直到现在我什至没有注意到第二个版本构建了一个列表,而不是一个数组。这让人想起以后如何使用数据的问题。现在,我假设它在看不见的代码中被转换为一个 numpy 数组。

最后,我猜这样的事情可能最方便:

dataAry = np.empty(TotalSamples)
i=0
for line in open(filename):
  words=line.split()
  repeats=int(words[2])
  value=float(words[0])
  np.copyto(dataAry[i:i+repeats],value)
  i+=repeats

肯定有某种方法可以使用 numpy 来计算索引,但我不知道像这样手动解析并使用 loadtxt 加载完整表(好吧,有趣的列)的成本更高。

于 2013-08-19T07:14:18.650 回答
1

我相信你可以用 替换你的 for 循环numpy.repeat(multiColAry[:, 0], multiColAry[:, 2]),这应该会有很大的不同。

此外,numpy 数组通常是 indexarray[i, j, k]而不是array[i][j][k],在这种情况下,结果应该是相同的,但在某些情况下,后者实际上会给你错误的结果。无论哪种情况,前者都应该更快。

最后,在使用 numpy 编程时,不鼓励使用元素操作和 for 循环。相反,鼓励使用数组或“矢量化”代码。在此范例中,您将程序表示为数组的操作,而不是对其元素的操作。Numpy 针对这种编程进行了优化。我知道这对于从 C 或 Java 等低级语言转过来的人来说是陌生的,但它类似于 Matlab 或 IDL 等其他科学编程语言。

于 2013-08-19T06:29:29.590 回答