Python - CSV: большой файл со строками разной длины - PullRequest
2 голосов
/ 31 марта 2010

Короче говоря, у меня есть 2000000 строк CSV-файл, который имеет различные длины строк. Это связано с архаичными регистраторами данных и проприетарными форматами. Мы получаем конечный результат в виде CSV-файла в следующем формате. Моя цель - вставить этот файл в базу данных postgres. Как я могу сделать следующее:

  • Сохраните первые 8 столбцов и мои последние 2 столбца, чтобы иметь согласованный файл CSV
  • Добавить новый столбец к файлу csv file в первой или последней позиции.

1, 2, 3, 4, 5, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, img_id.jpg, -50
1, 2, 3, 4, 5, 0,0,0,0,0,0,0,0,0, img_id.jpg, -50
1, 2, 3, 4, 5, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, img_id.jpg, -50
1, 2, 3, 4, 5, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, img_id.jpg, -50
1, 2, 3, 4, 5, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, img_id.jpg, -50
1, 2, 3, 4, 5, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, img_id.jpg, -50
1, 2, 3, 4, 5, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, img_id.jpg, -50
1, 2, 3, 4, 5, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, img_id.jpg, -50
1, 2, 3, 4, 5, 0,0,0,0,0,0, img_id.jpg, -50
1, 2, 3, 4, 5, 0,0,0,0,0,0,0,0,0,0,0 img_id.jpg, -50

Ответы [ 4 ]

7 голосов
/ 31 марта 2010

Читать строку с csv, затем:

newrow = row[:8] + row[-2:]

затем добавьте свое новое поле и запишите его (также с csv).

2 голосов
/ 31 марта 2010

Вы можете открыть файл как текстовый файл и читать строки по одной за раз. Есть ли кавычки или экранированные запятые, которые не "разделяют поля"? Если нет, вы можете сделать

with open('thebigfile.csv', 'r') as thecsv:
    for line in thecsv:
        fields = [f.strip() for f in thecsv.split(',')]
        consist = fields[:8] + fields[-2:] + ['onemore']
        ... use the `consist` list as warranted ...

Я подозреваю, что там, где у меня есть + ['onemore'], вы, возможно, захотите "добавить столбец", как вы говорите, с совершенно другим содержанием, но, конечно, я не могу догадаться, что это может быть.

Не отправляйте каждую строку отдельно со вставкой в ​​БД - для 20 миллионов вставок потребуется длинное время. Вместо этого сгруппируйте «согласованные» списки, добавив их во временный список - каждый раз, когда длина этого списка достигает, скажем, 1000, используйте executemany для добавления всех этих записей.

Редактировать : чтобы уточнить, я не рекомендую использовать csv для обработки файла, который вы знаете не в "правильном" формате csv: его непосредственная обработка дает вам более непосредственный контроль (особенно когда вы обнаруживаете другие неровности, отличные от запятой в строке).

1 голос
/ 31 марта 2010

Извините, вам нужно написать код с этим. Когда у вас есть такой огромный файл, стоит проверить его все, чтобы убедиться, что он соответствует ожидаемому. Если вы пропустите несчастные данные в свою базу данных, вы никогда не получите все это.

Помните странности о CSV: это путаница из множества похожих стандартов с различными правилами о цитировании, экранировании, нулевых символах, юникоде, пустых полях (",,,"), многострочные входы и пустые строки. Модуль csv имеет «диалекты» и опции, и вам может пригодиться класс csv.Sniffer.

Я рекомендую вам:

  • запустите команду 'tail', чтобы просмотреть последние несколько строк.
  • если это выглядит хорошо, запустите весь файл через csv reader, чтобы увидеть это ломает. Составьте быструю гистограмму «полей на строку».
  • Подумайте о «допустимых» диапазонах и типах символов и строго отметьте их как ты читаешь. Особенно следите за необычными юникодами или персонажами вне диапазон печати. ​​
  • Серьезно подумайте, хотите ли вы сохранить дополнительные нечетные значения в текстовом поле «остаток строки».
  • Бросить любые неожиданные строки в файл исключения.
  • Исправьте ваш код для обработки нового шаблона в файле исключений. Полоскание. Повторите.
  • Наконец, запустите все заново, фактически сбросив данные в базу данных.

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

Ваш окончательный код, вероятно, будет выглядеть следующим образом, но я не могу быть уверен, не зная ваших данных, особенно то, насколько они «хорошо себя ведут»:

while not eof
    out = []
    for chunk in range(1000):
       try:
          fields = csv.reader.next()
       except StopIteration:
          break
       except:
          print str(reader.line_num) + ", 'failed to parse'"
       try:
          assert len(fields) > 5 and len(fields < 12)
          assert int(fields[3]) > 0 and int(fields[3]) < 999999
          assert int(fields[4]) >= 1 and int(fields[4] <= 12) # date
          assert field[5] == field[5].strip()  # no extra whitespace
          assert not field[5].strip(printable_chars)  # no odd chars
          ...
       except AssertionError:
          print str(reader.line_num) + ", 'failed checks'"
       new_rec = [reader.line_num]  # new first item
       new_rec.extend(fields[:8])   # first eight
       new_rec.extend(fields[-2:])  # last two
       new_rec.append(",".join(field[8:-2])) # and the rest
       out.append(new_rec)
    if database:
       cursor.execute_many("INSERT INTO raw_table VALUES %d,...", out)

Конечно, ваш пробег зависит от этого кода. Это первый набросок псевдо-кода. Ожидайте написания твердого кода для ввода, который займет большую часть дня.

1 голос
/ 31 марта 2010

Я бы порекомендовал использовать модуль csv. Вот некоторый код, основанный на обработке CSV, который я сделал в другом месте

from __future__ import with_statement
import csv

def process( reader, writer):
    for line in reader:
        data = row[:8] + row[-2:]
        writer.write( data )

def main( infilename, outfilename ):
    with open( infilename, 'rU' ) as infile:
        reader = csv.reader( infile )
        with open( outfilename, 'w') as outfile:
            writer = csv.writer( outfile )
            process( reader, writer )

if __name__ == '__main__':
    if len(sys.argv) != 3:
        print "syntax: python process.py filename outname"
        sys.exit(1)
    main( sys.argv[1], sys.argv[2] )
...