【发布时间】:2019-11-04 23:54:31
【问题描述】:
我有一个 60k 行的数据文件,其中每行有 ~1k 逗号分隔的 Ints(我想立即变成 Doubles)。
我想遍历 32 行的随机“批次”序列,其中批次是所有行的随机子集,并且没有一个批次共享行。由于每批次有 60k 行和 32 行,所以应该有 1875 个批次。
如有必要,我愿意进行更改,但我希望它们采用延迟评估的(批次)列表的形式。需要这个的代码是foldM,我在其中使用它:
resulting_struct <- foldM fold_fn my_struct batch_list
以便它在当前累加器my_struct 和batch_list 的下一个元素的结果上重复调用fold_fn。
我很困惑。当我不需要洗牌时,这很容易;我只是将它们读入并分块,它们被懒惰地评估,所以我没有问题。现在我完全被卡住了,感觉我一定错过了一些简单的东西。
我尝试了以下方法:
将文件读入行列表并天真地打乱输入。这不起作用,因为
readFile被延迟评估,但它需要将整个文件读入内存以随机打乱它,它很快就会耗尽我所有的 ~8 GB RAM。获取文件的长度,然后创建一个从 0 到 60k 的混洗索引批次列表,这些列表对应于将被选择形成批次的行号。然后,当我想实际获取数据批次时,我会这样做:
ind_batches <- get_shuffled_ind_batches_from_file fname
batch_list <- mapM (get_data_batch_from_ind_batch fname) ind_batches
地点:
get_shuffled_ind_batches_from_file :: String -> IO [[Int]]
get_shuffled_ind_batches_from_file fname = do
contents <- get_contents_from_file fname -- uses readFile, returns [[Double]]
let n_samps = length contents
ind = [0..(n_samps-1)]
shuffled_indices <- shuffle_list ind
let shuffled_ind_chunks = take 1800 $ chunksOf 32 shuffled_indices
return shuffled_ind_chunks
get_data_batch_from_ind_batch :: String -> [Int] -> IO [[Double]]
get_data_batch_from_ind_batch fname ind_chunk = do
contents <- get_contents_from_file fname
let data_batch = get_elems_at_indices contents ind_chunk
return data_batch
shuffle_list :: [a] -> IO [a]
shuffle_list xs = do
ar <- newArray n xs
forM [1..n] $ \i -> do
j <- randomRIO (i,n)
vi <- readArray ar i
vj <- readArray ar j
writeArray ar j vi
return vj
where
n = length xs
newArray :: Int -> [a] -> IO (IOArray Int a)
newArray n xs = newListArray (1,n) xs
get_elems_at_indices :: [a] -> [Int] -> [a]
get_elems_at_indices my_list ind_list = (map . (!!)) my_list ind_list
然而,mapM 似乎立即评估,然后尝试重复读取文件内容(我认为,RAM 无论如何都会爆炸)。
- 多一点搜索告诉我,我可以尝试使用
unsafeInterleaveIO来制作它,以便它懒惰地评估一个动作,所以我尝试像这样坚持它:
get_data_batch_from_ind_batch :: String -> [Int] -> IO [[Double]]
get_data_batch_from_ind_batch fname ind_chunk = unsafeInterleaveIO $ do
contents <- get_contents_from_file fname
let data_batch = get_elems_at_indices contents ind_chunk
return data_batch
但没有运气,和上面一样的问题。
我觉得我一直在这里撞墙,一定错过了一些非常简单的事情。有人建议改用流或管道,但是当我查看它们的文档时,我并不清楚如何使用它们来解决这个问题。
如何在不耗尽所有内存的情况下读取大型数据文件并随机播放它?
【问题讨论】:
标签: file haskell input lazy-evaluation