Рассмотрим список, содержащий несколько элементов, каждый из которых содержит информацию об одних и тех же N
свойствах. Каждое из этих свойств может принимать определенное ограниченное (но, возможно, неизвестное) количество дискретных значений. Список не обязательно упорядочен.
Я хотел бы отсортировать эти элементы в N
-мерный массив, чтобы каждое свойство изменялось только вдоль своей оси, то есть было постоянным в пределах любого фрагмента вдоль этой оси.
Упрощенный пример:
# Three properties
prop_1 = ['01','02','03','07']
prop_2 = ['foo','bar','baz']
prop_3 = ['yellow','red']
from itertools import product
# Consider this as the input
string_list = ['_'.join(s) for s in product(prop_1, prop_2, prop_3)]
# HOWEVER...
from random import shuffle
# Inputs may be unsorted
shuffle(string_list)
Теперь я хочу организовать string_list
в массив формы (4,3,2)
так, чтобы первое свойство изменялось вдоль первой оси, & скоро. Таким образом, ожидаемый результат будет следующим:
array([[['01_foo_yellow','01_foo_red'],
['01_bar_yellow','01_bar_red'],
['01_baz_yellow','01_baz_red']],
[['02_foo_yellow','02_foo_red'],
['02_bar_yellow','02_bar_red'],
['02_baz_yellow','02_baz_red']],
[['03_foo_yellow','03_foo_red'],
['03_bar_yellow','03_bar_red'],
['03_baz_yellow','03_baz_red']],
[['07_foo_yellow','07_foo_red'],
['07_bar_yellow','07_bar_red'],
['07_baz_yellow','07_baz_red']]])
Каждое свойство будет постоянным в любом срезе вдоль своей оси, то есть:
A[3,...] # All strings containing '07' as property 1
A[:,1,:] # All strings containing 'bar' as property 2
A[...,0] # All strings containing 'yellow' as property 3
Метод должен быть устойчивым, даже если есть отсутствуют предметы. Например, если мы удалим '02_bar_yellow'
и '03_baz_red'
из входов, форма выходного массива должна остаться неизменной, с None
, где эти записи в противном случае были бы отсортированы:
array([[['01_foo_yellow','01_foo_red'],
['01_bar_yellow','01_bar_red'],
['01_baz_yellow','01_baz_red']],
[['02_foo_yellow','02_foo_red'],
[ None, '02_bar_red'],
['02_baz_yellow','02_baz_red']],
[['03_foo_yellow','03_foo_red'],
['03_bar_yellow','03_bar_red'],
['03_baz_yellow', None. ]],
[['07_foo_yellow','07_foo_red'],
['07_bar_yellow','07_bar_red'],
['07_baz_yellow','07_baz_red']]])
Проблема:
Хотя приведенное выше иллюстрирует общую идею, я на самом деле пытаюсь заставить это работать для набора match
объектов регулярных выражений, «свойствами» которых являются его группы захвата.
import re
pattern = '(\d+)_(\w+)_(\w+)'
regex = re.compile(pattern)
# Consider this as the input
matches = [re.match(s) for s in string_list]
Затем я хочу отсортировать в соответствии со значениями, заданными методом group()
каждого объекта match
.
Хотя это не совсем подходит к решению, я могу получить элементы, отсортированные по фрагментам вдоль оси с использованием itertools.groupby()
:
# Sort by the first capturing group
groupings = itertools.groupby(matches, key=lambda m: m.groups()[0])
grouped_strings = [[m.string for m in g] for n,g in groupings]
Таким образом, содержимое grouped_strings[3]
совпадает с содержимым фрагмента A[3,...]
, приведенным в первом примере. Однако эти записи представлены в виде уплощенного массива.
Мне приходит в голову, что я должен иметь возможность использовать itertools.groupby
итеративно для достижения правильной сортировки, но я не могу понять это. В то же время мне интересно, есть ли более простой или более «pythoni c» способ добиться этого.