При использовании библиотеки Pandas очень удобно загружать данные из разных источников, например из файлов с помощью функции read_csv. Все работает из коробки, много разных опций. Но если в данных, которые нужно загрузить, закралась ошибка, то тут каши не сваришь. Конечно, для анализа можно пренебречь некоторой частью данных при загрузке, то есть запустить функцию с параметром error_bad_lines=False. Тогда все строки, в которых есть ошибка, будут проигнорированы. Такой способ подходит для быстрого анализа или когда количество строк с ошибками несущественно по сравнению с размером данных. Но для точного анализа нужно загрузить все данные, то есть обработать ошибочные строки и запихнуть их в датафрейм.
Одна из возможных ошибок выглядит так
1 | pandas.io.common.CParserError: Error tokenizing data. C error: Expected 20 fields in line 47773, saw 22 |
что говорит нам следующее: количество столбцов в строке не совпадает с начальным, который pandas взял за основу (по первой строке).
Мне очень понравилось решение, которое я нашел на сайте stackoverflow.com
Смысл его в том, что каждую ошибку, на которой спотыкается pandas, ловим через Exception, обрабатываем и записываем строку, на которой произошел сбой. После отработки pandas всего файла, берутся все строки с ошибками и обрабатываются по своему желанию.
В данной реализации хорошо, что, во-первых, делается максимум работы с помощью встроенной функции read_csv, во-вторых, можно настроить парсинг на определенные ошибки, в-третьих, можно впихнуть свою функцию для парсинга данных.
Отработка ошибок делается в лоб: открывается файл, читаются только строки с ошибками, разбираются через свою функцию парсера, возвращается все в виде массива. После конкатируются два датафрейма.
Не стоит забывать, что в python3 нет нормального решения для чтения из файла определенной строки, поэтому сделано все по-простому через открытие файла и чтение строки через функцию readline
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | # python3 extended function to read csv in Pandas def pandasReadCsvExtended(files,sep=',', func_parser='', func_afterparser = ''): dataRez = pd.DataFrame() for file in files: line = [] dataFile = pd.DataFrame() cont = True while cont == True: try: dataFile = pd.read_csv(file, sep=sep, skiprows=line) cont = False except Exception as e: errormsg = e.args[0] errortype = errormsg.split('.')[0].strip() if errortype == 'Error tokenizing data': cerror = errormsg.split(':')[1].strip().replace(',', '') nums = [n for n in cerror.split(' ') if str.isdigit(n)] line.append(int(nums[1]) - 1) else: print('Unknown Error: {}'.format(errormsg)) if line != [] and callable(func_parser): fileIO = open(file, 'r') toDataFrame = [] ln_prev = 0 # count of columns in dataFrame header_count = dataFile.shape[1] for ln in line: lineSteps = ln - ln_prev tmp = ln_prev for i in range(lineSteps): tmp += 1 fileIO.readline() parts = func_parser(fileIO.readline().rstrip(), header_count) toDataFrame.append(parts) ln_prev = tmp + 1 dataErrors = pd.DataFrame.from_records(toDataFrame, columns=dataFile.columns.values.tolist()) dataFile = pd.concat([dataFile, dataErrors], ignore_index=True) if callable(func_afterparser): func_afterparser(dataFile, file) dataRez = pd.concat([dataRez, dataFile], ignore_index=True) return dataRez |