Pandas read_csv converter - как обрабатывать исключения (literal_eval SyntaxError) - PullRequest
0 голосов
/ 01 ноября 2018

В Pandas DataFrame я читаю CSV-файл, который выглядит следующим образом:

          A              B
  +--------------+---------------+
0 |              | ("t1", "t2")  |
  +--------------+---------------+
1 | ("t3", "t4") |               |
  +--------------+---------------+

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

df = pd.read_csv(my_file.csv, dtype=str, delimiter=',',
    converters={'A': ast.literal_eval, 'B': ast.literal_eval})

Преобразователь ast.literal_eval прекрасно работает для преобразования буквальных кортежей в объекты кортежей Python внутри кода - но только до тех пор, пока нет пустых ячеек. Поскольку у меня есть пустые ячейки, я получаю ошибку:

SyntaxError: неожиданный EOF при разборе

Согласно этому ответу S / O , я должен попытаться отловить исключение SyntaxError для пустых строк:

ast использует compile для компиляции исходной строки (которая должна быть выражение) в АСТ. Если исходная строка недопустима выражение (как пустая строка), ошибка синтаксиса будет вызвана компилировать.

Однако я не уверен, как отлавливать исключения для отдельных ячеек в контексте read_csv converters.

Как лучше всего это сделать? В противном случае есть какой-нибудь способ преобразовать пустые строки / ячейки в объекты, которые literal_eval будет принимать или игнорировать?

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

Ответы [ 2 ]

0 голосов
/ 04 ноября 2018

Я бы сначала прочитал данные как обычно, без literal_eval(). Это дает нам:

              A             B
0           NaN  ("t1", "t2")
1  ("t3", "t4")           NaN

Тогда я бы сделал это:

df.fillna('()').applymap(ast.literal_eval)

Что дает:

          A         B
0        ()  (t1, t2)
1  (t3, t4)        ()

Я думаю, что удобно иметь кортежи во всех ячейках, даже в пустых. Это облегчит работу с кортежами позже, например:

newdf.sum(axis=1)

Что дает вам:

0    (t1, t2)
1    (t3, t4)

Потому что "добавление" кортежей - это конкатенация. И даже сложнее, но все же очень полезно:

newdf.A.str[0]

Дает вам:

0    NaN
1     t3

Потому что pd.Series.str, несмотря на то, что он работает только со строками, прекрасно работает со списками и кортежами. Таким образом, вы можете эффективно и равномерно индексировать элементы в кортежах каждого столбца.

0 голосов
/ 04 ноября 2018

Вы можете создать пользовательскую функцию, которая условно использует ast.literal_eval:

from ast import literal_eval
from io import StringIO

# replicate csv file
x = StringIO("""A,B
,"('t1', 't2')"
"('t3', 't4')",""")

def literal_converter(val):
    # replace first val with '' or some other null identifier if required
    return val if val == '' else literal_eval(val)

df = pd.read_csv(x, delimiter=',', converters=dict.fromkeys('AB', literal_converter))

print(df)

          A         B
0            (t1, t2)
1  (t3, t4)          

Кроме того, вы можете использовать try / except, чтобы поймать SyntaxError. Это решение более мягкое, поскольку оно будет иметь дело с другим искаженным синтаксисом, т.е. SyntaxError / ValueError, вызванным причинами другими , чем пустыми значениями.

def literal_converter(val):
    try:
        return literal_eval(val)
    except SyntaxError, ValueError:
        return val
...