从Python中的巨大CSV文件中读取随机行

jbs*_*ssm 30 python csv random file

我有这个非常大的CSV文件(15 Gb),我需要读取大约100万条随机行.据我所知 - 并实现 - Python中的CSV实用程序只允许在文件中按顺序迭代.

将所有文件读入内存以使用一些随机选择是非常耗费内存的,并且通过所有文件并丢弃一些值并选择其他文件非常耗时,因此,无论如何都要从CSV文件中选择一些随机行并且只读那行?

我尝试没有成功:

   import csv

    with open('linear_e_LAN2A_F_0_435keV.csv') as file:
        reader = csv.reader(file)
        print reader[someRandomInteger]
Run Code Online (Sandbox Code Playgroud)

CSV文件的示例:

331.093,329.735 
251.188,249.994 
374.468,373.782 
295.643,295.159 
83.9058,0 
380.709,116.221 
352.238,351.891 
183.809,182.615 
257.277,201.302
61.4598,40.7106
Run Code Online (Sandbox Code Playgroud)

Mar*_*ina 27

import random

filesize = 1500                 #size of the really big file
offset = random.randrange(filesize)

f = open('really_big_file')
f.seek(offset)                  #go to random position
f.readline()                    # discard - bound to be partial line
random_line = f.readline()      # bingo!

# extra to handle last/first line edge cases
if len(random_line) == 0:       # we have hit the end
    f.seek(0)
    random_line = f.readline()  # so we'll grab the first line instead
Run Code Online (Sandbox Code Playgroud)

正如@AndreBoos指出的那样,这种方法会导致偏向选择.如果您知道线的最小和最大长度,则可以通过执行以下操作来消除此偏差:

让我们假设(在这种情况下)我们有min = 3和max = 15

1)找到前一行的长度(Lp).

然后,如果Lp = 3,则该线最偏向.因此,如果Lp = 15,我们应该100%的时间,这条线最偏向于.我们应该只占20%的时间,因为选择的可能性要高5倍.

我们通过随机保持X%的时间来实现这一点:

X = min/Lp

如果我们不保留这条线,我们会做另一个随机选择,直到我们的骰子卷好.:-)

  • 聪明,但这将为具有可变长度线的文件提供有偏见的结果. (4认同)
  • @ thg435:这不能解决任何问题.与短线之后的线相比,长线之后的线将不成比例地表示.此外,第一行将_never_被读取. (4认同)
  • @MariaZverina我认为要做出无偏的选择,你不仅要知道线的最小长度,还要知道文件中每个线长的频率.无论如何,在文件中,只有少于7个字符的值是0.我将尝试将我的文件转换为0替换为0.00000,然后您的例程应该完美. (3认同)
  • @MariaZverina,同意 jbssm 的评论。要校正偏差,您需要知道线长的分布,然后相应地进行归一化。如果线长 3 ... 15 的分布是均匀的,那么您的归一化技术就起作用了,但是要知道这种分布实际上是均匀的,我们需要读取整个文件。这让我们回到了相同的起始方块,如果我们至少要读取一次文件,为什么不将它写回到更适合将来快速操作的地方(例如,在固定字段中或作为 sqlite3 表,等等。) (2认同)
  • @MariaZverina ......好吧......我“做了数学”,使用你的第二步过滤器,概率确实是一致的。我站着纠正。(脱帽向你鞠躬) (2认同)

Sha*_*hin 10

我有这个非常大的CSV文件(15 Gb),我需要读取大约100万条随机行

假设您不需要确切的 100万行,并且事先知道CSV文件中的行数,您可以使用油藏采样来检索随机子集.只需遍历您的数据,每行确定选择行的机会.这样,您只需要传递一次数据.

如果您需要经常提取随机样本但实际数据集不经常更改(因为您每次数据集更改时只需跟踪条目数),这种方法效果很好.

chances_selected = desired_num_results / total_entries
for line in csv.reader(file):
   if random() < chances_selected:
        result.append(line)
Run Code Online (Sandbox Code Playgroud)

  • @ thg435,因此语句*“ ...,并知道CSV文件中的行数” *。如果您经常进行采样,并且只需要对数据集大小进行一次计数,则这种方案会起作用。 (2认同)

jte*_*ace 7

您可以使用概率方法的变体来选择文件中的随机行.

您可以保留大小的缓冲区,而不是只保留一个被选中的数字C.对于每个行号,n在带有N行的文件中,您希望选择具有概率的行C/n(而不是原始行1/n.如果选择了该数,则从C长度缓冲区中选择一个随机位置以逐出.

以下是它的工作原理:

import random

C = 2
fpath = 'somelines.txt'
buffer = []

f = open(fpath, 'r')
for line_num, line in enumerate(f):
    n = line_num + 1.0
    r = random.random()
    if n <= C:
        buffer.append(line.strip())
    elif r < C/n:
        loc = random.randint(0, C-1)
        buffer[loc] = line.strip()
Run Code Online (Sandbox Code Playgroud)

这需要次传递文件(因此它是线性时间)并返回文件中的精确 C行.每条线都有C/N被选中的概率.

为了验证上述是否有效,我创建了一个包含a,b,c,d,e的5行文件.我用C = 2运行代码10,000次.这应该产生5选择2(所以10)可能选择的均匀分布.结果:

a,b: 1046
b,c: 1018
b,e: 1014
a,c: 1003
c,d: 1002
d,e: 1000
c,e: 993
a,e: 992
a,d: 985
b,d: 947
Run Code Online (Sandbox Code Playgroud)


小智 5

如果您想多次抓取随机行(例如,用于机器学习的小批量),并且您不介意扫描一次大文件(而不将其加载到内存中),那么您可以创建一个行索引列表并使用seek 快速抓取线条(基于Maria Zverina 的回答)。

# Overhead:
# Read the line locations into memory once.  (If the lines are long,
# this should take substantially less memory than the file itself.)
fname = 'big_file'
s = [0]
linelocs = [s.append(s[0]+len(n)) or s.pop(0) for n in open(fname)]
f = open(fname) # Reopen the file.

# Each subsequent iteration uses only the code below:
# Grab a 1,000,000 line sample
# I sorted these because I assume the seeks are faster that way.
chosen = sorted(random.sample(linelocs, 1000000))
sampleLines = []
for offset in chosen:
  f.seek(offset)
  sampleLines.append(f.readline())
# Now we can randomize if need be.
random.shuffle(sampleLines)
Run Code Online (Sandbox Code Playgroud)

  • 根据您的回答,我构建了一个小包,用于读取文件中的任意行。在这里查看(https://github.com/jegesh/python-random-access-file-reader) (2认同)