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

时间:2012-05-30 15:56:58

标签: python file csv random

我有这个相当大的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]

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

13 个答案:

答案 0 :(得分:26)

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

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

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

1)找出前一行的长度(Lp)。

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

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

X = min / Lp

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

答案 1 :(得分:10)

  

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

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

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

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

答案 2 :(得分:7)

您可以使用probabilistic method的变体来选择文件中的随机行。

您可以保留一个大小为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()

这需要单个传递文件(因此它是线性时间)并从文件中返回完全 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

答案 3 :(得分:4)

如果你想多次抓取随机行(例如,机器学习的迷你批次),你不介意扫描一次巨大的文件(不加载到内存中),那么你可以创建一个列表线条凹痕和使用寻求快速抓住线条(基于玛丽亚·泽维纳的回答)。

# 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)

答案 4 :(得分:2)

如果这些行是真正的.csv格式而不是固定字段,那么不,没有。您可以遍历文件一次,索引每行的字节偏移量,然后在以后只需要使用索引集时,但是没有办法预先确定任意csv文件的行终止\ n字符的确切位置。

答案 5 :(得分:2)

如果您知道行总数 - 可以生成100万个随机数(random.sample(xrange(n), 1000000))直到作为一组的总行数,则可以使用另一种解决方案,然后使用:

for i, line in enumerate(csvfile):
    if i in lines_to_grab:
        yield line

这将以无偏见的方式为您提供100万行,但您需要事先获得行数。

答案 6 :(得分:1)

如果您可以将此数据放在sqlite3数据库中,那么选择一些随机行是微不足道的。您无需预先读取或填充文件中的行。由于sqlite数据文件是二进制文件,因此您的数据文件将比CSV文本小1/3到1/2。

您可以使用THIS之类的脚本导入CSV文件,或者更好的是,首先将数据写入数据库表。 SQLITE3是Python发行版的一部分。

然后使用这些语句获得1,000,000个随机行:

mydb='csv.db'
con=sqlite3.connect(mydb)

with con:
    cur=con.cursor()
    cur.execute("SELECT * FROM csv ORDER BY RANDOM() LIMIT 1000000;")

    for row in cur.fetchall():
        # now you have random rows...

答案 7 :(得分:0)

您可以使用固定长度的记录重写该文件,然后在中间文件上执行随机访问:

ifile = file.open("inputfile.csv")
ofile = file.open("intermediatefile.csv",'w')
for line in ifile:
    ofile.write(line.rstrip('\n').ljust(15)+'\n')

然后,你可以这样做:

import random
ifile = file.open("intermediatefile.csv")
lines = []
samples = random.sample(range(nlines))
for sample in samples:
    ifile.seek(sample)
    lines.append(ifile.readline())

需要更多的磁盘空间,并且第一个程序可能需要一些时间才能运行,但它允许随后无限制地随机访问第二个记录。

答案 8 :(得分:0)

# pass 1, count the number of rows in the file
rowcount = sum(1 for line in file)
# pass 2, select random lines
file.seek(0)
remaining = 1000000
for row in csv.reader(file):
    if random.randrange(rowcount) < remaining:
        print row
        remaining -= 1
    rowcount -= 1

答案 9 :(得分:0)

在这种方法中,我们生成一个随机数集,其元素数等于要读取的行数,其范围是数据中存在的行数。然后将其从最小到最大排序并存储。

然后逐行读取csv文件,并使用line_counter表示行号。然后使用已排序的随机数列表的第一个元素检查此line_counter,如果它们相同,则将该特定行写入新的csv文件,并从列表中删除第一个元素,并且先前的第二个元素需要第一个和周期的地方继续。

import random
k=random.sample(xrange(No_of_rows_in_data),No_of_lines_to_be_read)
Num=sorted(k)    
line_counter = 0

with open(input_file,'rb') as file_handle:
    reader = csv.reader(file_handle)
    with open(output_file,'wb') as outfile:
            a=csv.writer(outfile)
            for line in reader:
                line_counter += 1
                if line_counter == Num[0]:
                a.writerow(line)
                Num.remove(Num[0])
                if len(Num)==0:
                break    

答案 10 :(得分:0)

如果您可以使用pandasnumpy,我已经发布了pandas特定但非常有效的solution in another question

import pandas as pd
import numpy as np

filename = "data.csv"
sample_size = 1000000
batch_size = 5000

rng = np.random.default_rng()

sample_reader = pd.read_csv(filename, dtype=str, chunksize=batch_size)

sample = sample_reader.get_chunk(sample_size)

for chunk in sample_reader:
    chunk.index = rng.integers(sample_size, size=len(chunk))
    sample.loc[chunk.index] = chunk

有关更多详细信息,请please see the other answer

答案 11 :(得分:0)

def random_line(path, hint=1):
    with open(path, mode='rb') as file:
        import random
        while file.seek(random.randrange(file.seek(-2, 2))) and not file.readline(hint).endswith(b'\n'):
            pass
        return file.readline().decode().strip()

这是我为从一个非常大的文件中读取随机行而写的内容。

时间复杂度为 O(k) ,k 是文本文件中行的平均长度。

hint 参数是文本文件中行的最小长度,如果你事先知道,用它来加速函数。

答案 12 :(得分:0)

总是对我有用

import csv
import random
randomINT = random.sample(range(1, 72655), 40000)
with open(file.csv,"rU") as fp:
    reader = csv.reader(fp, delimiter=",", quotechar='"', dialect=csv.excel_tab)
    data_read = [row for idx, row in enumerate(reader) if idx in randomINT]
    for idx, line in enumerate(data_read):
        pass