Подтвердить что ты не робот

Прочитайте случайные строки из огромного файла CSV в Python

У меня есть довольно большой файл CSV (15 Гб), и мне нужно прочитать около 1 миллиона случайных строк. Насколько я могу видеть - и реализовать - утилита CSV в Python только позволяет последовательно итератировать в файле.

Очень много памяти, чтобы прочитать весь файл в памяти, чтобы использовать какой-то случайный выбор, и очень много времени, чтобы пройти через весь файл и отбросить некоторые значения и выбрать другие, поэтому есть в любом случае выбор случайного строка из файла 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
4b9b3361

Ответ 1

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% случаев, так как он скорее всего выбран.

Выполняем это случайным образом, сохраняя прямую X% времени, когда:

X = min/Lp

Если мы не будем держать линию, мы делаем еще один случайный выбор, пока наш бросок кости не станет хорошим.: -)

Ответ 2

У меня есть этот довольно большой файл CSV (15 Гб), и мне нужно прочитать около 1 миллиона случайных строк из него

Предполагая, что вам не нужно точно 1 миллион строк и заранее знать количество строк в вашем CSV файле, вы можете использовать выборки коллектора для извлечения вашего случайного подмножества. Просто итетерируйте свои данные, и для каждой линии определите шансы выбора линии. Таким образом вам нужен только один проход ваших данных.

Это хорошо работает, если вам нужно часто извлекать случайные выборки, но фактический набор данных изменяется редко (так как вам нужно будет отслеживать количество записей каждый раз при изменении набора данных).

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

Ответ 3

Вы можете использовать вариацию вероятностного метода для выбора случайной строки в файле.

Вместо того, чтобы просто сохранить один выбранный номер, вы можете сохранить буфер размером 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.

Чтобы убедиться, что это работает, я создал файл с 5 строками, содержащими a, b, c, d, e. Я пробежал код 10 000 раз с C = 2. Это должно привести к равномерному распределению 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

Ответ 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)

Ответ 5

Если строки - это действительно .csv-формат и НЕ фиксированное поле, то нет, нет. Вы можете просканировать файл через один раз, индексируя смещения байтов для каждой строки, а затем, когда позже нужно использовать только набор индексов, но нет способа априорно предсказать точное местоположение символа \n для завершения строки для произвольных файлов csv.

Ответ 6

Другое решение возможно, если вы знаете общее количество строк - сгенерируйте 1 миллион случайных чисел (random.sample(xrange(n), 1000000)) до общего количества строк в качестве набора, а затем используйте:

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

Это даст вам ровно 1 миллион строк беспристрастно, но вам нужно иметь количество строк заранее.

Ответ 7

Если вы можете поместить эти данные в базу данных sqlite3, выбор некоторого количества случайных строк тривиален. Вам не нужно предварительно читать или писать строки в файле. Так как файлы данных sqlite являются двоичными, файл данных будет на 1/3 до 1/2 меньше, чем текст CSV.

Вы можете использовать script как ЭТО для импорта 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...

Ответ 8

Вы можете переписать файл с фиксированными записями, а затем выполнить произвольный доступ к промежуточному файлу позже:

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

Требуется больше места на диске, и для первой программы может потребоваться некоторое время для запуска, но она позволяет неограниченный более поздний случайный доступ к записям со вторым.

Ответ 9

# 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

Ответ 10

В этом методе мы генерируем случайное числовое число, число элементов которого равно числу строк, которые должны быть считаны, причем его диапазон представляет собой количество строк, присутствующих в данных. Затем он сортируется от наименьшего до самого большого и сохраняется.

Затем файл 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