Haskell 如何读取大的csv文件?
我试图读取haskell的一个大csv文件,并按每列生成字数 这将导致文件中超过4M行 因此,我选择读取一个块,并获得每次的字数(一个块5k行)。 然后把它加起来 当我用12000行和120000行测试函数时,时间几乎呈线性增长。 但当读取180000行时,运行时间超过四倍以上 我认为这是因为内存不够,用磁盘交换会使功能慢很多 我必须以map/reduce风格编写代码,但是如何使haskell不在内存中保存所有数据呢 打击是我的代码和分析结果Haskell 如何读取大的csv文件?,haskell,Haskell,我试图读取haskell的一个大csv文件,并按每列生成字数 这将导致文件中超过4M行 因此,我选择读取一个块,并获得每次的字数(一个块5k行)。 然后把它加起来 当我用12000行和120000行测试函数时,时间几乎呈线性增长。 但当读取180000行时,运行时间超过四倍以上 我认为这是因为内存不够,用磁盘交换会使功能慢很多 我必须以map/reduce风格编写代码,但是如何使haskell不在内存中保存所有数据呢 打击是我的代码和分析结果 import Data.Ord import Tex
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
我以前曾用另一种语言遇到过这个问题。诀窍不是将数据读入内存,而是一次只读取一行数据。当您阅读下一行时,只需覆盖您的变量,因为您只需要查找字数。 只需在io流中测试EOF文件结束条件,然后退出即可。这样你就不需要了;我不必拆分文件
希望这能有所帮助。首先,我有几点建议
无论您在积累什么,这些都会给您带来良好的性能和恒定的内存使用模数。有几件事需要注意:
lazy csv
包来实现这一点。然而,仍然很容易在不经意间保留将所有输入保留在内存中的引用。更好的选择是使用流媒体库,如csv-conduct
或pipes-csv
ByteString
或Text
foldata
函数中建立起来——字数表达式似乎没有减少- 使用惰性IO
- 将
包与(lazy)lazy csv
一起使用,而不是ByteString
String
- 使用
来限制行数的计算模式
- 使用未装箱数组来保存列计数器
{-# LANGUAGE BangPatterns #-}
import qualified Data.ByteString.Lazy.Char8 as BS
import Data.ByteString.Lazy (ByteString)
import Text.CSV.Lazy.ByteString
import System.Environment (getArgs)
import Data.List (foldl')
import Data.Int
import Data.Array.IO
import Data.Array.Unboxed
import Control.Monad
type Length = Int64 -- use Int on 32-bit systems
main = do
(arg:_) <- getArgs
(line1:lns) <- fmap BS.lines $ BS.readFile arg
-- line1 contains the header
let (headers:_) = [ map csvFieldContent r | r <- csvTable (parseCSV line1) ]
ncols = length headers :: Int
arr <- newArray (1,ncols) 0 :: IO (IOUArray Int Length)
let inc i a = do v <- readArray arr i; writeArray arr i (v+a)
let loop !n [] = return n
loop !n (b:bs) = do
let lengths = map BS.length $ head [ map csvFieldContent r | r <- csvTable (parseCSV b) ]
forM_ (zip [1..] lengths) $ \(i,a) -> inc i a
loop (n+1) bs
print headers
n <- loop 0 lns
putStrLn $ "n = " ++ show (n :: Int)
arr' <- freeze arr :: IO (UArray Int Length)
putStrLn $ "totals = " ++ show arr'
{-#语言模式}
将限定数据.ByteString.Lazy.Char8作为BS导入
导入Data.ByteString.Lazy(ByteString)
导入Text.CSV.Lazy.ByteString
导入System.Environment(getArgs)
导入数据列表(foldl')
导入数据.Int
导入Data.Array.IO
导入Data.Array.unbox
进口管制
type Length=Int64——在32位系统上使用Int
main=do
(arg:u)您需要使用流媒体库,如csv conduct
或pipes csv
“当您阅读下一行时,只需覆盖您的变量,因为您只需要查找字数。”这是Haskell。我们没有“变量”
{-# LANGUAGE BangPatterns #-}
import qualified Data.ByteString.Lazy.Char8 as BS
import Data.ByteString.Lazy (ByteString)
import Text.CSV.Lazy.ByteString
import System.Environment (getArgs)
import Data.List (foldl')
import Data.Int
import Data.Array.IO
import Data.Array.Unboxed
import Control.Monad
type Length = Int64 -- use Int on 32-bit systems
main = do
(arg:_) <- getArgs
(line1:lns) <- fmap BS.lines $ BS.readFile arg
-- line1 contains the header
let (headers:_) = [ map csvFieldContent r | r <- csvTable (parseCSV line1) ]
ncols = length headers :: Int
arr <- newArray (1,ncols) 0 :: IO (IOUArray Int Length)
let inc i a = do v <- readArray arr i; writeArray arr i (v+a)
let loop !n [] = return n
loop !n (b:bs) = do
let lengths = map BS.length $ head [ map csvFieldContent r | r <- csvTable (parseCSV b) ]
forM_ (zip [1..] lengths) $ \(i,a) -> inc i a
loop (n+1) bs
print headers
n <- loop 0 lns
putStrLn $ "n = " ++ show (n :: Int)
arr' <- freeze arr :: IO (UArray Int Length)
putStrLn $ "totals = " ++ show arr'