如何阅读大csv文件?

Jul*_*ang 3 haskell

我试图通过haskell读取一个大的csv文件,并按每列生成单词count.

这个文件中有超过4M的行.

所以我选择读取一个块并每次获得字数(每行5k行).而不是总结它.

当我用12000行和120000行测试函数时,时间增加几乎是线性的.但是当读取180000行时,运行时间超过四次.

我认为这是因为内存不够,与磁盘交换使功能慢得多.

我把我的代码编写为map/reduce风格,但是如何让haskell不能将所有数据保存在内存中?

打击是我的代码和分析结果.

import Data.Ord
import Text.CSV.Lazy.String
import Data.List
import System.IO
import Data.Function (on)
import System.Environment

splitLength = 5000


mySplit' [] = []
mySplit' xs = [x] ++  mySplit' t 
    where
    x = take splitLength xs 
    t = drop splitLength xs                

getBlockCount::Ord a => [[a]] -> [[(a,Int)]]
getBlockCount t =   map 
    (map (\x -> ((head x),length x))) $ 
    map group $ map sort $ transpose t

foldData::Ord a=> [(a,Int)]->[(a,Int)]->[(a,Int)]
foldData lxs rxs = map combind wlist
    where
        wlist = groupBy ((==) `on` fst) $ sortBy (comparing fst) $ lxs ++ rxs
        combind xs 
         | 1==(length xs) = head xs
         | 2 ==(length xs) = (((fst . head) xs ), ((snd . head) xs)+((snd . last) xs))


loadTestData datalen = do
    testFile <- readFile "data/test_csv"
    let cfile = fromCSVTable $ csvTable $ parseCSV testFile
    let column = head cfile
    let body = take datalen $ tail cfile
    let countData = foldl1' (zipWith  foldData)  $ map  getBlockCount  $ mySplit' body 
    let output =  zip column $ map ( reverse . sortBy (comparing snd) ) countData
    appendFile "testdata" $ foldl1 (\x y -> x ++"\n"++y)$ map show $tail output

main = do 
    s<-getArgs
    loadTestData $ read  $ last s
Run Code Online (Sandbox Code Playgroud)

剖析结果

loadData +RTS -p -RTS 12000

total time  =        1.02 secs   (1025 ticks @ 1000 us, 1 processor)
total alloc = 991,266,560 bytes  (excludes profiling overheads)

 loadData +RTS -p -RTS 120000

total time  =       17.28 secs   (17284 ticks @ 1000 us, 1 processor)
total alloc = 9,202,259,064 bytes  (excludes profiling overheads)



   loadData +RTS -p -RTS 180000

total time  =       85.06 secs   (85059 ticks @ 1000 us, 1 processor)
total alloc = 13,760,818,848 bytes  (excludes profiling overheads)
Run Code Online (Sandbox Code Playgroud)

bit*_*app 11

首先,提出一些建议.

  1. 列表并不快.好吧,好吧,缺点是恒定时间,但总的来说,列表并不快.你正在使用列表.(对于两端的消费和消费,Data.Sequence会更快)

  2. 字符串很慢.字符串很慢,因为它们是[Char](字符列表).您当前使用的库是根据字符串列表编写的.通常,链接列表的链接列表不是您想要的文本处理.这不是bueno.在将来使用Text(for,呃,text)或ByteString(for bytes)而不是String,除非它是小而且不是性能敏感的.

  3. 您正在使用的库只是懒惰而不是流式传输.您必须处理将流式行为叠加到惰性语义上以获得常量内存使用.流式库解决了递增处理数据和限制内存使用的问题.我建议为一般类问题学习Pipes或Conduit.一些特定于问题的库还将提供可用于流式传输的iteratee API.Iteratee API可以直接使用或连接到Pipes/Conduit/etc.

我不认为你使用的图书馆是个好主意.

我建议你使用以下库之一:

http://hackage.haskell.org/package/pipes-csv(基于管道)

https://hackage.haskell.org/package/cassava-0.4.2.0/docs/Data-Csv-Streaming.html(通用CSV库,不基于特定的流媒体库)

https://hackage.haskell.org/package/csv-conduit(基于管道)

这些应该为你提供良好的性能和恒定的内存使用模数,无论你可能积累什么.