我有这个相当大的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
答案 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)
如果您可以使用pandas
和numpy
,我已经发布了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