Преобразование строкового представления многоиндексных панд в многоиндексных панд в python - PullRequest
0 голосов
/ 03 июля 2018

У меня есть строковое представление мультииндекса ниже.

iterables = [['bar', 'baz', 'foo', 'qux'], ['one', 'two']]
df = pd.MultiIndex.from_product(iterables, names=['first', 'second'])
df = str(df)

Я хотел бы преобразовать представленную строку df обратно в pandas multiIndex class . Есть ли в пандах какие-либо прямые функции для того же самого?

Исключенный выход:

print(df)
MultiIndex(levels=[['bar', 'baz', 'foo', 'qux'], ['one', 'two']],
       labels=[[0, 0, 1, 1, 2, 2, 3, 3], [0, 1, 0, 1, 0, 1, 0, 1]],
       names=['first', 'second'])

Заранее спасибо.

1 Ответ

0 голосов
/ 03 июля 2018

Строковое представление MultiIndex - это почти исполняемый код, поэтому вы можете оценить его с помощью eval, например:

eval(df, {}, {'MultiIndex': pd.MultiIndex})
# MultiIndex(levels=[[u'bar', u'baz', u'foo', u'qux'], [u'one', u'two']],
#        labels=[[0, 0, 1, 1, 2, 2, 3, 3], [0, 1, 0, 1, 0, 1, 0, 1]],
#        names=[u'first', u'second'])

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

Кроме того, вот безопасный и простой, но несколько хрупкий способ сделать это:

import ast
# convert df into a literal string defining a dictionary
dfd = (
    "{" + df[11:-1] + "}"
        .replace("levels=", "'levels':")
        .replace("labels=", "'labels':")
        .replace("names=", "'names':") 
)
# convert it safely into an actual dictionary
args = ast.literal_eval(dfd)
# use the dictionary as arguments to pd.MultiIndex
pd.MultiIndex(**args)

При использовании этого кода произвольные строки не могут вызвать сбой в работе вашего компьютера, поскольку ast.literal_eval() не допускает никаких операторов, только буквальные выражения.

Вот версия, которая безопасна и не требует предварительного указания имен аргументов, но она более сложна:

import ast, tokenize
from cStringIO import StringIO
tokens = [  # make a list of mutable tokens
    list(t) 
    for t in tokenize.generate_tokens(StringIO('{' + df[11:-1] + '}').readline)
]
for t, next_t in zip(tokens[:-1], tokens[1:]):
    # convert `identifier=` to `'identifier':`
    if t[0] == 1 and next_t[0] == 51 and next_t[1] == '=':
        t[0] = 3                  # switch type to quoted string
        t[1] = "'" + t[1] + "'"   # put quotes around identifier
        next_t[1] = ':'           # convert '=' to ':' 
args = ast.literal_eval(tokenize.untokenize(tokens))
pd.MultiIndex(**args)

Обратите внимание, что этот код будет вызывать исключение, если df искажен или содержит «identifier = ...» как код (не внутри строк) на более низких уровнях. Но я не думаю, что это может случиться с str(MultiIndex). Если это проблема, вы можете сгенерировать дерево ast для исходной строки df, затем извлечь аргументы и программно преобразовать их в буквальное определение для dict ({x: y}, а не dict(x=y)), затем используйте ast.literal_eval, чтобы оценить это.

...