NumPy "record array" или "structured array" или "recarray"

Что, если есть, является разницей между "структурированным массивом" NumPy, "массивом записи" и "повторением"?

NumPy docs подразумевают, что первые два одинаковы: если они есть, что является предпочтительным термином для этого объекта?

В той же документации говорится (внизу страницы): Вы можете найти дополнительную информацию о рекурсиях и структурированных массивах (включая разницу между ними) здесь. Есть ли простое объяснение этой разницы?

Ответ 1

Записи/ретрансляции реализованы в

https://github.com/numpy/numpy/blob/master/numpy/core/records.py

Некоторые релевантные кавычки из этого файла

Рекордные массивы Рекордные массивы отображают поля структурированных массивов как свойства. Повторная репликация почти идентична стандартной матрице (которая поддерживает    уже названные поля). Самое большое различие заключается в том, что он может использовать    атрибут-поиск, чтобы найти поля, и он построен с использованием    запись.

recarray является подклассом ndarray (таким же образом, что matrix и masked arrays). Но учтите, что конструктор отличается от np.array. Это больше похоже на np.empty(size, dtype).

class recarray(ndarray):
    """Construct an ndarray that allows field access using attributes.
    This constructor can be compared to ``empty``: it creates a new record
       array but does not fill it with data.

Ключевая функция для реализации уникального поля в качестве поведения атрибута __getattribute__ (__getitem__ реализует индексирование):

def __getattribute__(self, attr):
    # See if ndarray has this attr, and return it if so. (note that this
    # means a field with the same name as an ndarray attr cannot be
    # accessed by attribute).
    try:
        return object.__getattribute__(self, attr)
    except AttributeError:  # attr must be a fieldname
        pass

    # look for a field with this name
    fielddict = ndarray.__getattribute__(self, 'dtype').fields
    try:
        res = fielddict[attr][:2]
    except (TypeError, KeyError):
        raise AttributeError("recarray has no attribute %s" % attr)
    obj = self.getfield(*res)

    # At this point obj will always be a recarray, since (see
    # PyArray_GetField) the type of obj is inherited. Next, if obj.dtype is
    # non-structured, convert it to an ndarray. If obj is structured leave
    # it as a recarray, but make sure to convert to the same dtype.type (eg
    # to preserve numpy.record type if present), since nested structured
    # fields do not inherit type.
    if obj.dtype.fields:
        return obj.view(dtype=(self.dtype.type, obj.dtype.fields))
    else:
        return obj.view(ndarray)

Сначала он пытается получить обычный атрибут - такие вещи, как .shape, .strides, .data, а также все методы (.sum, .reshape и т.д.). В противном случае он будет искать имя в именах полей dtype. Таким образом, это действительно просто структурированный массив с некоторыми переопределенными методами доступа.

Насколько я могу сказать, record array и recarray совпадают.

Другой файл показывает что-то из истории

https://github.com/numpy/numpy/blob/master/numpy/lib/recfunctions.py

Сбор утилит для управления структурированными массивами. Большинство из этих функций были первоначально реализованы Джоном Хантером для Matplotlib. Они были переписаны и расширены для удобства.

Многие из функций этого файла заканчиваются на:

    if asrecarray:
        output = output.view(recarray)

Тот факт, что вы можете вернуть массив в виде recarray, показывает, как "тонкий" этот слой.

numpy имеет долгую историю и объединяет несколько независимых проектов. Мое впечатление, что recarray - это более старая идея, а структурированные массивы - текущая реализация, построенная на обобщенном dtype. recarrays кажутся сохраненными для удобства и обратной совместимости, чем любая новая разработка. Но мне пришлось бы изучить историю файлов github, а также любые недавние проблемы/запросы на загрузку.

Ответ 2

В двух словах вы должны использовать структурированные массивы, а не повторные массивы, потому что структурированные массивы работают быстрее, и единственное преимущество этих массивов состоит в том, что вы можете писать arr.x вместо arr['x'], что может быть удобным сочетанием клавиш., но также подвержен ошибкам, если имена ваших столбцов конфликтуют с множественными методами/атрибутами.

Смотрите этот отрывок из книги @jakevdp для более подробного объяснения. В частности, он отмечает, что простой доступ к столбцам структурированных массивов может быть примерно в 20–30 раз быстрее, чем доступ к столбцам повторных массивов. Тем не менее, его пример использует очень маленький фрейм данных с 4 строками и не выполняет никаких стандартных операций.

Для простых операций на больших фреймах данных разница, вероятно, будет намного меньше, хотя структурированные массивы все еще быстрее. Например, здесь представлен структурированный массив записей, каждый из которых содержит 10 000 строк (код для создания массивов из кадра данных, заимствованный из ответа @jpp, приведен здесь).

n = 10_000
df = pd.DataFrame({ 'x':np.random.randn(n) })
df['y'] = df.x.astype(int)

rec_array = df.to_records(index=False)

s = df.dtypes
struct_array = np.array([tuple(x) for x in df.values], dtype=list(zip(s.index, s)))

Если мы выполним стандартную операцию, такую как умножение столбца на 2, то для структурированного массива это будет примерно на 50% быстрее:

%timeit struct_array['x'] * 2
9.18 µs ± 88.9 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

%timeit rec_array.x * 2
14.2 µs ± 314 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)