Как сделать ruamel.yaml дамп отформатированных массивов numpy в виде простого списка

#python-3.x #numpy #ruamel.yaml

#python-3.x #numpy #ruamel.yaml

Вопрос:

Я бы хотел иметь ruamel.yaml выгружает массив numpy (1-D, 2-D или n-D), но я бы хотел, чтобы он был отформатирован удобным для чтения способом. Я знаю, что есть ряд похожих вопросов, но даже с ними я не смог решить эту проблему. В идеале это выглядело бы примерно так:

 data:
  oneD: 
    [0.44, 0.49, 0.73, 0.46, 0.12, 0.98, 0.91, 0.5 , 0.1 , 0.26, 0.89, 0.02, 0.51]
  twoD: 
    [[0.44, 0.49, 0.73, 0.46, 0.12, 0.98, 0.91, 0.5 ],
     [0.11, 0.4 , 0.51, 0.71, 0.27, 0.47, 0.1 , 0.74],
     [0.36, 0.56, 0.85, 0.02, 0.93, 0.19, 0.72, 0.96]]
  threeD: 
    [[[0.17, 0.46, 0.9 ],
      [0.75, 0.97, 0.32],
      [0.95, 0.39, 0.26]],

     [[0.06, 0.3 , 0.23],
      [0.78, 0.14, 0.89],
      [0.47, 0.72, 0.02]]]

 

Я попытался выполнить это с помощью удобной функции array2string от numpy и регистрации нового представителя для формы ndarray, показанной здесь:

 def numpyRepresenter(dumper, data):
    asStr = np.array2string(data, formatter={'float_kind':lambda x: "%.3f" % x}, separator=', ', threshold=int(1e10), max_line_width=120)
    return dumper.represent_scalar(u'tag:yaml.org,2002:str', asStr, style='|')
 

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

Я также пытался просто заставить представителя преобразовать массивы np в списки и распечатать их, но форматирование не такое приятное или понятное, особенно для данных с разными десятичными знаками.

Возможно ли это сделать с помощью ruamel или есть лучший подход, который я должен использовать?

Полный MWE здесь

 import numpy as np
import ruamel.yaml

def numpyRepresenter(dumper, data):
    asStr = np.array2string(data, formatter={'float_kind':lambda x: "%.2f" % x}, separator=', ', threshold=int(1e10), max_line_width=120)
    return dumper.represent_scalar(u'tag:yaml.org,2002:str', asStr, style='|')

yaml = ruamel.yaml.YAML()

test = yaml.load("""
data:
  oneD: 
    [0.44, 0.49, 0.73, 0.46, 0.12, 0.98, 0.91, 0.5 , 0.1 , 0.26, 0.89, 0.02, 0.51]
  twoD: 
    [[0.44, 0.49, 0.73, 0.46, 0.12, 0.98, 0.91, 0.5 ],
     [0.11, 0.4 , 0.51, 0.71, 0.27, 0.47, 0.1 , 0.74],
     [0.36, 0.56, 0.85, 0.02, 0.93, 0.19, 0.72, 0.96]]
  threeD: 
    [[[0.17, 0.46, 0.9 ],
      [0.75, 0.97, 0.32],
      [0.95, 0.39, 0.26]],

     [[0.06, 0.3 , 0.23],
      [0.78, 0.14, 0.89],
      [0.47, 0.72, 0.02]]]
""")

oneD = 5*np.array(test['data']['oneD'])
twoD = np.array(test['data']['twoD'])**(2/3)
threeD = np.sqrt(np.array(test['data']['threeD']))

test['data']['oneD'] = oneD
test['data']['twoD'] = twoD
test['data']['threeD'] = threeD

yaml.default_flow_style = None
yaml.default_style = None
yaml.representer.add_representer(np.ndarray, numpyRepresenter)
with open('testOut.yaml', 'w') as fp:
    yaml.dump(test, fp)
 

Комментарии:

1. arr.tolist() это самый быстрый и самый общий способ создания списка из массива. Я подозреваю, что такие списки являются лучшим форматом для записи yaml и перезагрузки. Однако, как вы обнаружили, списки не выстраивают столбцы красиво. Возможно, вы захотите взглянуть на код np.savetxt , чтобы увидеть, как он форматирует массивы в виде аккуратных строк / столбцов. Ключ в том, что он создает строку формата и выполняет fmt%tuple(row) для каждого row из 2d-массива.

2. represent_scalar — это не тот путь. Я бы хотел записать помеченную конструкцию YAML и в представителе создать специальный список CommentedSeq элементов, содержащих ScalarFloat s. Затем специальный список должен выполнить перенос строк. Кстати, нет смысла загружать с помощью загрузчика в оба конца, а затем устанавливать .default_flow_style атрибут в качестве отдельного CommentedSeq/Map элемента, содержащего информацию о потоке в их .fa атрибуте.

3. Я думаю, что понимаю концепцию, но я не совсем понимаю, как ее реализовать. Ваше предположение, что я мог бы сохранить форматирование так, как мне нравится, сбросив его в качестве комментария? Я до сих пор не смог выяснить, какой тег выдать, чтобы убедить дампер обработать его как комментарий P.S. спасибо за ruamel.yaml! это отлично работало для меня много лет.