Если данные имеют фиксированный размер, но хранятся как vector<double>
, то они обрабатываются так, как если бы они не были фиксированного размера. Up root всегда будет читать их как неровные массивы, и поэтому метод asarray
, описанный в другом вопросе, недоступен.
Тем не менее, если у вас больше знаний, чем у метаданных вашего файла, и вы хотите попробовать неподдерживаемый хак, вы можете заставить интерпретацию вашего vector<double>
всегда иметь три элемента. Вот пример - во-первых, нам нужен подходящий ROOT файл:
TFile *f = new TFile("tmp.root", "recreate");
TTree *t = new TTree("t", "t");
std::vector<double> x;
t->Branch("x", &x);
x = {101, 102, 103};
t->Fill();
x = {201, 202, 203};
t->Fill();
x = {301, 302, 303};
t->Fill();
x = {401, 402, 403};
t->Fill();
x = {501, 502, 503};
t->Fill();
x = {601, 602, 603};
t->Fill();
t->Write();
f->Close();
Теперь, обычный способ прочитать это в Up root с помощью JaggedArray:
>>> import uproot
>>> branch = uproot.open("tmp.root")["t"]["x"]
>>> branch.array()
<JaggedArray [[101.0 102.0 103.0] [201.0 202.0 203.0] [301.0 302.0 303.0] [401.0 402.0 403.0] [501.0 502.0 503.0] [601.0 602.0 603.0]] at 0x7f325ab4eb90>
>>> branch.interpretation
asjagged(asdtype('>f8'), 10)
Но это выделяет новые массивы каждый раз, когда вы читаете его, и выполняет некоторые манипуляции, чтобы найти границы, которые, как вы знаете, являются регулярными. (И до root этого не знает.)
Длина заголовка для вектора STL составляет 10 байтов. После этого его содержимое сериализуется по порядку, от первого до последнего, прежде чем перейти к следующему вектору STL. Для трех 8-байтовых чисел с плавающей запятой это 10 + 8 + 8 + 8 = 34 байта, всегда одинакового размера. Тот факт, что он всегда имеет одинаковый размер, имеет решающее значение для следующего.
A NumPy структурированный массив может представлять массивы структур фиксированного размера в виде dtype:
>>> array = branch.array(uproot.asdtype([("header", "S10"), ("x", ">f8"), ("y", ">f8"), ("z", ">f8")]))
>>> array
array([(b'@\x00\x00\x1e\x00\t\x00\x00\x00\x03', 101., 102., 103.),
(b'@\x00\x00\x1e\x00\t\x00\x00\x00\x03', 201., 202., 203.),
(b'@\x00\x00\x1e\x00\t\x00\x00\x00\x03', 301., 302., 303.),
(b'@\x00\x00\x1e\x00\t\x00\x00\x00\x03', 401., 402., 403.),
(b'@\x00\x00\x1e\x00\t\x00\x00\x00\x03', 501., 502., 503.),
(b'@\x00\x00\x1e\x00\t\x00\x00\x00\x03', 601., 602., 603.)],
dtype=[('header', 'S10'), ('x', '<f8'), ('y', '<f8'), ('z', '<f8')])
Нам не нравится смотреть на заголовок, поэтому мы можем вырезать его, используя обычную NumPy вырезку:
>>> array[["x", "y", "z"]]
array([(101., 102., 103.), (201., 202., 203.), (301., 302., 303.),
(401., 402., 403.), (501., 502., 503.), (601., 602., 603.)],
dtype={'names':['x','y','z'], 'formats':['<f8','<f8','<f8'], 'offsets':[10,18,26], 'itemsize':34})
(или просто попросить array["x"]
, чтобы получить не структурированный массив). Поскольку мы можем сделать это с простым uproot.asdtype
, мы можем сделать это с предварительно выделенным uproot.asarray
.
>>> import numpy as np
>>> big = np.empty(1000, dtype=[("header", "S10"), ("x", ">f8"), ("y", ">f8"), ("z", ">f8")])
>>> branch.array(uproot.asarray(big.dtype, big))
array([(b'@\x00\x00\x1e\x00\t\x00\x00\x00\x03', 101., 102., 103.),
(b'@\x00\x00\x1e\x00\t\x00\x00\x00\x03', 201., 202., 203.),
(b'@\x00\x00\x1e\x00\t\x00\x00\x00\x03', 301., 302., 303.),
(b'@\x00\x00\x1e\x00\t\x00\x00\x00\x03', 401., 402., 403.),
(b'@\x00\x00\x1e\x00\t\x00\x00\x00\x03', 501., 502., 503.),
(b'@\x00\x00\x1e\x00\t\x00\x00\x00\x03', 601., 602., 603.)],
dtype=[('header', 'S10'), ('x', '>f8'), ('y', '>f8'), ('z', '>f8')])
. Выше, big
должен быть по крайней мере настолько большим, насколько набор данных вы хотите чтение и чтение возвращают усеченное представление этого массива. Он не выделяет новый массив - данные хранятся в big
:
>>> big[["x", "y", "z"]][:10]
array([( 1.01000000e+002, 1.02000000e+002, 1.03000000e+002),
( 2.01000000e+002, 2.02000000e+002, 2.03000000e+002),
( 3.01000000e+002, 3.02000000e+002, 3.03000000e+002),
( 4.01000000e+002, 4.02000000e+002, 4.03000000e+002),
( 5.01000000e+002, 5.02000000e+002, 5.03000000e+002),
( 6.01000000e+002, 6.02000000e+002, 6.03000000e+002),
( 1.22164945e-309, 5.26335088e-310, 1.22167067e-309),
(-5.70498984e+158, 5.97958175e+158, -5.97958175e+158),
(-4.92033505e+032, -4.92033505e+032, -4.92033505e+032),
( 3.77957352e+011, 3.77957221e+011, 3.77957320e+011)],
dtype={'names':['x','y','z'], 'formats':['>f8','>f8','>f8'], 'offsets':[10,18,26], 'itemsize':34})
Все, кроме 6 прочитанных событий, является неинициализированным мусором, поэтому я рекомендую использовать усеченный вывод из * Функция 1037 *; это просто для того, чтобы показать, что big
заполняется - вы не получаете новый массив.
Согласно вашему вопросу (2), если данные не регулярны (в количестве байтов на запись) тогда ты не сможешь это сделать. Кроме того, помните, что вышеупомянутая методика официально не поддерживается: вы должны знать, что ваши данные являются регулярными, и вы должны знать, что векторы STL имеют 10-байтовый заголовок, чего мы не ожидаем от большинства пользователей.