Доступ к элементам неудобного массива, которые не являются переданным индексом

Я пытаюсь получить доступ к элементам неудобного массива, которые не соответствуют какому-то конкретному набору индексов. Всего у меня 3 события по одному джету на событие и некоторому количеству лептонов. С каждым лептоном связан определенный флаг. Для каждой струи я отслеживаю индексы лептонов в этой струе:

jet_lepton_indices = ak.Array([[0, 2], [1], [2,3]])
print(f'jet_lepton_indices\n{jet_lepton_indices}\n')

lepton_flags = ak.Array([[0, 10, 20, 30], [0, 10, 20, 30], [0, 10, 20, 30, 40]])
print(f'lepton_flags\n{lepton_flags}\n')

Выход:

jet_lepton_indices
[[0, 2], [1], [2, 3]]

lepton_flags
[[0, 10, 20, 30], [0, 10, 20, 30], [0, 10, 20, 30, 40]]

Если мне нужны флаги лептонов, находящихся в каждом самолете, я делаю lepton_flags[jet_lepton_indices] и получаю:

[[0, 20],
 [10],
 [20, 30]]

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

[[10, 30],
 [0, 20, 30],
 [0, 10, 40]]

Я думал, что смогу lepton_flags[~jet_lepton_indices], но такое поведение мне непонятно. Как сгладить/разгладить, я тоже не могу этого понять.

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
0
107
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

jet_lepton_indices = [[0, 2], [1], [2, 3]] 
lepton_flags = [[0, 10, 20, 30], [0, 10, 20, 30], [0, 10, 20, 30, 40]]

flags_in_tot  = []
flags_out_tot = []

for index in range(len(jet_lepton_indices)):
    indices = jet_lepton_indices[index]
    flags = lepton_flags[index]
    flags_in = []
    flags_out = []
    for f in range(len(flags)):
        if f in indices:
            flags_in.append(flags[f])
        else:
            flags_out.append(flags[f])
    flags_in_tot.append(flags_in)
    flags_out_tot.append(flags_out)

flags_in_tot
[[0, 20], [10], [20, 30]]

flags_out_tot
[[10, 30], [0, 20, 30], [0, 10, 40]]

Спасибо за это ясное объяснение @igmartin! Однако одно из преимуществ неудобного массива (неудобно-array.org/doc/main) заключается в том, что вы можете выполнять вычисления без использования циклов, так же, как и с массивами numpy. Неудобные массивы позволяют создавать «зубчатые» массивы, где данные не являются простыми массивами размером n x m. Ваше объяснение идеально подходит для небольших объемов данных, когда цикл не замедляет работу, но не всегда масштабируется для больших объемов данных. Еще раз спасибо за этот комментарий! Я уверен, что это окажется полезным для других!

Matt Bellis 23.06.2024 02:35

Правильно: хотя это и даст правильный ответ, оно будет неприятно медленным, если размер массива превышает тысячи или миллионы. Я считаю, что этот пример небольшой, чтобы продемонстрировать проблему, а не ожидаемый масштаб данных.

Jim Pivarski 23.06.2024 16:20
Ответ принят как подходящий

(«TL;DR» находится внизу, под горизонтальной линией.)

~ (побитовое нет) не работало с вашим массивом целых чисел, потому что оно просто инвертировало биты в целых числах:

>>> jet_lepton_indices
<Array [[0, 2], [1], [2, 3]] type='3 * var * int64'>
>>> ~jet_lepton_indices
<Array [[-1, -3], [-2], [-3, -4]] type='3 * var * int64'>

В конечном итоге вам нужно преобразовать срез целочисленного массива в срез логического массива. Как срезы, целочисленные массивы содержат строго больше информации, чем логические массивы: они могут дублировать и изменять порядок элементов срезанного массива в дополнение к простому удалению элементов. Таким образом, срезы целочисленного массива всегда можно преобразовать в срезы логического массива, но не наоборот. Фактически, был запрос на такую ​​функцию, #497, и в этой проблеме описано несколько способов ее получения, каждый из которых отличается от того, который я разработал ниже. (Я все еще собираюсь показать пример, который я только что разработал, потому что он проще и демонстрирует общую схему: cartesian чтобы увеличить измерения, сделайте что-то в новом измерении, затем агрегируйте его, чтобы вернуться к старому количеству измерений. )

Еще один факт о срезах логических массивов заключается в том, что они должны согласовываться с длиной списка массива, который они срезают. (Сноска: чтобы инвертировать выборку, вам нужно знать универсальный набор, поэтому инвертировать можно только срез логического массива.) Следовательно, чтобы преобразовать срез целочисленного массива в срез логического массива, нам нужно использовать массив нарезать, lepton_flags. Мы можем использовать ak.local_index, чтобы создать целочисленный массив целочисленных индексов для всех элементов, существующих в lepton_flags:

>>> all_indices = ak.local_index(lepton_flags)
>>> all_indices
<Array [[0, 1, 2, 3], [0, ..., 3], [0, 1, 2, 3, 4]] type='3 * var * int64'>

Теперь цель будет состоять в том, чтобы найти логические значения для каждого из этих индексов, которые говорят, находится ли индекс в jet_lepton_indices или нет. Такой вопрос имеет форму: «Для каждого элемента в X (локальный индекс) существует ли какой-либо элемент в Y (jet_lepton_indices), для которого Z (они равны)?» «Для каждого» одного массива с другим массивом обрабатывается ak.cartesian , и поскольку мы хотим агрегировать все, что связано с одним элементом X («является ли ak.any item равным? ") нам понадобится nested=True, чтобы создать новое измерение, чтобы позже агрегировать его.

>>> pairs = ak.cartesian([all_indices, jet_lepton_indices], nested=True)
>>> pairs.show(type=True)
type: 3 * var * var * (
    int64,
    int64
)
[[[(0, 0), (0, 2)], [(1, 0), (1, 2)], [(2, ...), ...], [(3, 0), (3, 2)]],
 [[(0, 1)], [(1, 1)], [(2, 1)], [(3, 1)]],
 [[(0, 2), (0, 3)], [(1, 2), (1, 3)], ..., [(3, ...), ...], [(4, 2), (4, 3)]]]

Пары более глубоко вложены (var * var *), чем all_indices и jet_lepton_indices (var *), потому что мы попросили сгруппировать результаты по одинаковому первому индексу (nested=True).

Левый элемент в каждой из этих пар — от all_indices, правый — от jet_lepton_indices для всех комбинаций. Чтобы разделить их, используйте ak.unzip:

>>> whole_set, in_set = ak.unzip(pairs)
>>> whole_set
<Array [[[0, 0], [1, 1], [...], [3, 3]], ...] type='3 * var * var * int64'>
>>> in_set
<Array [[[0, 2], [0, 2], [...], [0, 2]], ...] type='3 * var * var * int64'>

whole_set и in_set совпадают, потому что они происходят от одного и того же pairs. Поскольку они выстраиваются в линию, мы можем использовать к ним ==, чтобы получить логическое значение True тогда и только тогда, когда член whole_set находится в in_set.

>>> whole_set == in_set
<Array [[[True, False], ..., [False, ...]], ...] type='3 * var * var * bool'>

Если какой-либо (ak.any) из этих самых внутренних списков (axis=-1) равен True, то мы хотим сказать, что вся группа, представляющая элемент из all_indices, находится в jet_lepton_indices.

>>> jet_lepton_boolean = ak.any(whole_set == in_set, axis=-1)
>>> jet_lepton_boolean
<Array [[True, False, True, False], ..., [False, ...]] type='3 * var * bool'>

Это jet_lepton_boolean — логический массив, который можно использовать как срез для создания тех же элементов, что и jet_lepton_indices. Как логическое значение, его можно отрицать с помощью ~.

>>> lepton_flags[~jet_lepton_boolean]
<Array [[10, 30], [0, 20, 30], [0, 10, 40]] type='3 * var * int64'>

Это выбор lepton_flags, который вам нужен: это все, кроме того, что было в

>>> lepton_flags[jet_lepton_indices]
<Array [[0, 20], [10], [20, 30]] type='3 * var * int64'>

В качестве альтернативы вы могли бы создать отрицательные логические значения напрямую, используя != вместо ==.


Вот краткое описание этого метода как функции:

def indices_to_booleans(indices, array_to_slice):
    whole_set, in_set = ak.unzip(ak.cartesian([
        ak.local_index(array_to_slice), indices
    ], nested=True))
    return ak.any(whole_set == in_set, axis=-1)

Это решение зависит от того факта, что ваши исходные массивы имеют глубину только одного уровня (var *), хотя я думаю, что оно может быть обобщено, если вы передадите соответствующий аргумент axis в ak.cartesian, но я недостаточно об этом думал, чтобы быть уверенным.

Кроме того, #497 предлагает больше способов сделать это.

Другие вопросы по теме