2016-08-25 4 views
3

Использование Python 2.7, я хочу, чтобы преобразовать строку в следующем формате списка списков:Преобразование строки в список списков

>>> value = "[[1 0 0 0] [0 1 0 0] [0 0 1 0] [0 0 0 1]]" 
>>> ... 
>>> result = [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]] 

мне удалось сделать это, но, кажется, неаккуратно ,

>>> value = "[[1 0 0 0] [0 1 0 0] [0 0 1 0] [0 0 0 1]]" 
>>> val2 = value.replace('[[', '').replace(']]', '').split('] [') 
>>> val2 
['1 0 0 0', '0 1 0 0', '0 0 1 0', '0 0 0 1'] 
>>> val_final = [[float(x) for x in x] for x in [x.replace(' ', '') for x in val2]] 
>>> val_final 
[[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]] 

Есть ли лучше, чище способ сделать это, что было бы более надежным?

Примечание: Я ожидаю, что в строке будут иметь только целые или плавающие значения (контекст: это матрица 4x4, соответствующая позиции объекта в трехмерной среде).


Edit: Альтернативное значение может содержать поплавки, как так:

>>> value = "[[9.231 -0.123 -2 0] [0 1 0 0] [0 0 1 0] [0 0 0 1]]" 
>>> ... 
>>> result = [[9.231, -0.123, -2.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]] 

ответ

6

Использование json.loads из стандартной библиотеки, с небольшим количеством препарата (замещать пространства с запятыми) и просить его, чтобы преобразовать все найденные целые числа как поплавки:

>>> import json 
>>> value = "[[1 0 0 0] [0 1 0 0] [0 0 1 0] [0 0 0 1]]" 
>>> result = json.loads(value.replace(' ', ','), parse_int=float) 
[[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]] 

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

+0

Спасибо за предложение @Johan Dahlin. Я сделал сравнение с 'timeit', а' eval' принимает '0.2819679792258256', а' json.loads' принимает '0.038535186478554806'. Учитывая, что мне нужно будет преобразовать тысячи значений, это более быстрое решение. –

+0

В вашем ответе просто отсутствует преобразование в значения поплавка. –

+1

@ HEADLESS_0NE Я обновил, чтобы преобразовать все числа в float, но это может быть немного медленнее при преобразовании большего набора данных. Для повышения производительности переключите библиотеку json на что-то вроде https://github.com/esnme/ultrajson –

4

Вы можете использовать replace + eval:

value = "[[1 0 0 0] [0 1 0 0] [0 0 1 0] [0 0 0 1]]" 
result = eval(value.replace(" ", ",")) 
result 
#output: 
[[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]] 

И для преобразования целых чисел поплавка:

[list(map(float, l)) for l in result] #if you're using python 2 you can remove the "list" wrapping the "map" 
#output 
[[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]] 
+1

Не знаю, почему это было downvoted. Люди видят eval и боятся, но это хорошее решение. IMO –

+0

Это точно соответствует мне. Спасибо! –

+1

@AdamHughes У них есть причина бояться ... в прошлый раз, когда я видел использование 'eval', Martijn Pieters много писал о том, почему вы должны избегать этого и альтернативы, но я потерял этот пост. –

0
value = "[[1 0 0 0] [0 1 0 0] [0 0 1 0] [0 0 0 1]]" 
eval(value.replace(" ", ",")) 

Выход:

[[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]] 

для возвращения поплавка значения

import numpy as np 
l=np.array(eval(value.replace(" ", ",")))+0. 
l.tolist() 

Выход

[[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]] 
1

Ну, самым гибким и расширяемым способом было бы реализовать LL(1) parser. Однако это требует немного работы. Вот грубый реализация в Python 2.7:

value = "[[1 0 0 0] [0 1 0 0] [0 0 1 0] [0 0 0 1]]" 

class ParseError(ValueError): 
    pass 

def tokenize(s): 
    buf = "" 

    while s: 
     if s[0] == '[': 
      yield s[0] 
     elif s[0].isalnum(): 
      buf += s[0] 
     elif s[0] == ']': 
      if buf: 
       yield buf 
       buf = "" 
      yield ']' 
     elif s[0] == ' ': 
      if buf: 
       yield buf 
       buf = "" 
     else: 
      raise ParseError() 
     s = s[1:] 

def parse_array(tokens): 
    if tokens[0] != '[': 
     raise ParseError() 
    tokens = tokens[1:] 
    elements = [] 
    while tokens[0] != ']': 
     element, tokens = parse(tokens) 
     elements.append(element) 

     if not tokens: 
      raise ParseError() 
    return elements, tokens[1:] 

def parse_number(tokens): 
    return float(tokens[0]), tokens[1:] 

def parse(tokens): 
    if tokens[0] == '[': 
     return parse_array(tokens) 
    elif tokens[0].isalnum(): 
     return parse_number(tokens) 
    else: 
     raise ParseError() 

tokens = list(tokenize(value)) 
print tokens 
# ['[', '[', '1', '0', '0', '0', ']', '[', '0', '1', '0', '0', ']', '[', '0', '0', '1', '0', ']', '[', '0', '0', '0', '1', ']', ']'] 
parsed, tokens = parse(tokens) 
print parsed 
# [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]] 

Примечание: не тестировал все крайние случаи, но это должно дать представление о том, как это может выглядеть.

Здесь я предполагаю, что:

  • может быть любое количество пробелов между числами или скобки
  • скобки не должны следовать пробел
  • формат представляет jagged arrays (они могут иметь строки разной длины) любых измерений (они могут быть произвольно вложенными).

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

Есть два шага:

  1. лексемизация: преобразует строку в последовательность символов (скобок, количество и потенциально других вещи, как идентификаторы).
  2. Анализ: преобразование последовательности символов в дереве выражений. Для этого вам нужно прочитать один символ впереди, прежде чем знать, как интерпретировать следующие символы (таким образом, 1 в LL1).
1

Подобный ответ на Юхан Далин в:

Вместо использования JSon парсер, вы можете пользовательский встроенный Python Abstract Syntax Trees анализатор:

Как это:

value = "[[1 0 0 0] [0 1 0 0] [0 0 1 0] [0 0 0 1]]" 

import ast 

matrix = ast.literal_eval(value.replace(" ", ", ")) 
print(matrix) 
# -> [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]] 

С поплавками значения:

# -> [[9.231, -0.123, -2, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]] 

К "сила" преобразование в поплавка, просто написать:

matrix = [[float(x) for x in row] for row in matrix] 
print(matrix) 
# -> [[9.231, -0.123, -2.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]]