Python как индексировать многомерный массив строковым ключом, например dict

Я хотел бы совместить функциональность и NumPy это array С , а именно создание многомерного массива, который может быть индексирован строками.

например, я мог бы сделать это:

dict_2d = {'a': {'x': 1, 'y': 2},
           'b': {'x': 3, 'y': 4}}
print dict_2d['a','y']  # returns 2

Я знаю, что мог бы сделать dict_2d['a']['x'] но в долгосрочной перспективе я хотел бы иметь возможность рассматривать их как массивы numpy, включая матричное умножение и такое, и это невозможно с многослойными диктами.

его также не так сложно написать простой версия класса, где я просто использую класс для преобразования всех строк в индексы int, а затем использую numpy, но я хотел бы использовать то, что уже существует, если это возможно.

Edit: мне не нужна невероятная производительность. Я буду работать с массивами 10x10. Моя цель - сделать написание кода простым и надежным. Работа с массивами numpy не сильно отличается от записи в Fortran. Я провел достаточно своей жизни, отслеживая ошибки индексирования Fortran...

3 ответов


возможно, вы ищете панды, который предоставляет удобные типы данных, которые обертывают массивы numpy, позволяя вам обращаться к строкам и столбцам по имени, а не только по номеру.


Я не люблю давать готовые ответы - но я думаю, что это займет гораздо больше времени, чтобы объяснить это по-английски -

основная идея fetch объекты, как numpy делает, чтобы настроить __getitem__ method-значения, разделенные запятыми, представлены методу как кортежи - вы просто используете значения в кортежах в качестве индексов для ваших вложенных словарей последовательно.

помимо этого, Python упростил создание полнофункциональных эквивалентов dict с помощью коллекции.abc классы: если вы реализуете минимальный набор методов при inhetiring из collections[.abc].MutableMapping, все поведение словаря эмулируется - (__getitem__, __setitem__, __delitem__, __iter__, __len__) - тогда это просто вопрос правильной итерации ключевых компонентов и создания новых, пустых, регулярных словарей для хранения необходимых значений.

try:
    from collections import MutableMapping
except ImportError:
    # Python3 compatible import
    from collections.abc import MutableMapping

class NestedDict(MutableMapping):
    def __init__(self, *args, **kw):
        self.data = dict(*args, **kw)

    def get_last_key_levels(self, key, create=False):
        if not isinstance(key, tuple):
            key = (key,)
        current_data = self.data
        for subkey in key:
            previous = current_data
            current_data = current_data[subkey] if not create else current_data.setdefault(subkey, {})
        return previous, current_data, subkey

    def __getitem__(self, key):
        previous, current_data, lastkey = self.get_last_key_levels(key)
        return current_data

    def __setitem__(self, key, value):
        previous, current_data, lastkey = self.get_last_key_levels(key, True)
        previous[lastkey] = value

    def __delitem__(self, key):
        previous, current_data, lastkey = self.get_last_key_levels(key)
        del previous[lastkey]

    def __iter__(self):
        return iter(self.data)

    def __len__(self):
        return len(self.data)

    def __repr__(self):
        return "NestedDict({})".format(repr(self.data))

и вы готовы отправиться:

>>> from nesteddict import NestedDict
>>> x = NestedDict(a={})
NestedDict({'a': {}})
>>> x["a", "b"] = 10
>>> x
NestedDict({'a': {'b': 10}})
>>> x["a", "c", "e"]  = 25
>>> x
NestedDict({'a': {'c': {'e': 25}, 'b': 10}})
>>> x["a", "c", "e"] 
25
>>> 

обратите внимание, что это реализация высокого уровня, которая будет просто работать, но у вас не будет нигде рядом с оптимизацией уровень вы получите на NumPy с этим-наоборот. Если вам нужно будет выполнять быстрые операции с данными в этих объектах, вы можете проверить "cython" - или прибегнуть к своей идее транспонирования ключей dict в nuemric keys и использовать NumPy (эта идея все еще может выбрать некоторые идеи из этого ответа)


использовать панд Допустим, файл выглядит так: