#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! это отлично работало для меня много лет.