Изменение типа данных (dtype) для tabular.tabarray или numpy.recarray - PullRequest
1 голос
/ 29 ноября 2011

Я хочу представить данные в виде таблицы в Python.Думая "ну, кто-то наверняка написал такой модуль!"Я пошел в PyPI, где нашел Tabular, который оборачивает речевые массивы NumPy мощными функциями манипулирования данными.Большой!К сожалению, это не похоже на электронную таблицу, когда дело касается строк.

>>> import tabular as tb
>>> t = tb.tabarray(records=[('bork', 1, 3.5), ('stork', 2, -4.0)], names=['a','b','c'])
>>> t
tabarray([('bork', 1, 3.5), ('stork', 2, -4.0)], 
      dtype=[('a', '|S5'), ('b', '<i8'), ('c', '<f8')])
>>> t['a'][0] = 'gorkalork, but not mork'
>>> t
tabarray([('gorka', 1, 3.5), ('stork', 2, -4.0)], 
      dtype=[('a', '|S5'), ('b', '<i8'), ('c', '<f8')])

Хм ... tabarray!Вы обрезали мою строку там!В самом деле?!NumPy dtype '| S5' означает, что это строка из 5 или менее символов, но давай!Обновите dtype.Переформатируйте весь столбец, если это необходимо.Без разницы.Но не молча выбрасывайте мои данные!

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

>>> t.addrecords(('mushapushalussh', 3, 4.44))
tabarray([('gorka', 1, 3.5), ('stork', 2, -4.0), ('musha', 3, 4.44)], 
      dtype=[('a', '|S5'), ('b', '<i8'), ('c', '<f8')])

Я попытался вырезать весь столбец, изменить его тип, задать значение и переназначить его:

>>> firstcol_long = firstcol.astype('|S15')
>>> firstcol_long
tabarray(['gorka', 'stork'], 
      dtype='|S15')
>>> firstcol_long[0] = 'morkapork'
>>> firstcol_long
tabarray(['morkapork', 'stork'], 
      dtype='|S15')
>>> t['a'] = firstcol_long
>>> t
tabarray([('morka', 1, 3.5), ('stork', 2, -4.0)], 
      dtype=[('a', '|S5'), ('b', '<i8'), ('c', '<f8')])
>>> 

Он правильно присваивает значения, но исходный тип данных все еще действует, и мои ранее правильные данные снова молча усекаются.Я даже попытался установить явный тип данных:

>>> t = tb.tabarray(records=[('bork', 1, 3.5), ('stork', 2, -4.0)], dtype=[('a', str),('b', int),('c', float)])
>>> t
tabarray([('', 1, 3.5), ('', 2, -4.0)], 
      dtype=[('a', '|S0'), ('b', '<i8'), ('c', '<f8')])

Господи!Это еще хуже!Он правильно отображал типы int и float, но предположил, что str означал, что я хотел строки длиной 0, и обрезал все данные до нуля.Короче говоря, таблица не только не работает как электронная таблица, я не могу найти способ заставить ее работать.Производительность не является большой проблемой для меня.В моих электронных таблицах может быть не более сотен или тысяч строк, и я бы с радостью попросил систему немного скопировать данные, чтобы облегчить мой код.Табличные, кажется, во многих других отношениях очень хорошо отвечают всем требованиям.

Полагаю, я мог бы создать подкласс табличной таблицы с чем-то, что по умолчанию для всех строк будет иметь что-то невероятно большое (скажем, 1024 или 4096 байт), с помощью метода __setitem__, который вызывает исключение в случае назначения большей строки.Скорее небрежно ... но есть ли лучшие альтернативы?Я немного разбирался в numpy.recarray и так далее, и не видел ясного пути ... но я буду первым, кто признает, что я совершенно неопытен в NumPy.Реальность такова, что программы манипулирования данными могут увеличить длину строк сверх их первоначального максимума.Конечно, высокофункциональные модули должны это учитывать."Просто обрежь это!"Подход, распространенный в ориентированных на записи базах данных 1974 года, не может быть подходящим для Python в 2011 году!

Мысли и предложения?

Ответы [ 2 ]

6 голосов
/ 30 ноября 2011

Как один из разработчиков таблиц ... Я должен сказать, что я думаю, что первый ответчик ударил по голове гвоздем.

OP, поведение «усечения», о котором вы сожалеете, является фундаментальной проблемой NumPy, на которой основан Tabular. Но не совсем точно сказать, что это «ошибка», которая должна быть исправлена, это скорее «ограничение», отражающее / усиливающее весь смысл NumPy (и Tabular) для начала.

Как отметил первый отвечающий, NumPy предъявляет абсолютные требования к тому, чтобы структуры данных были однородными по своему размеру. После того, как вы выделите пустой массив данного типа данных, массив должен остаться таким типом данных - или иначе, новый массив с новой памятью должен быть инициализирован. В случае строковых типов данных длина строки является неотъемлемой фиксированной частью типа данных - вы не можете просто «преобразовать» массив строк длины N в массив строк длины М.

Фиксированные типы данных имеют решающее значение для того, как NumPy достигает огромного прироста производительности по сравнению со стандартными объектами Python. Это связано с тем, что с фиксированными типами данных объекты NumPy знают, сколько байтов было выделено для каждого объекта, и могут просто «перепрыгнуть» в пространство памяти туда, где должна быть данная запись, без необходимости считывать и обрабатывать это содержимое все промежуточные записи, в отличие от списков Python. Конечно, это ограничивает виды объектов, которые могут естественным образом БЫТЬ массивами-пустышками ... или, на самом деле, это ограничивает виды операций, которые можно выполнять с массивами-пустышками. В отличие от списка Python, который является полностью изменяемым (например, вы можете заменить любой элемент любым другим объектом Python, не нарушая выделения памяти для всех других объектов в списке), вы не можете преобразовать значение массива numpy в объект объекта другой тип данных - потому что как тогда будет работать байтный учет? Если вдруг N-й элемент становится больше, чем все остальные элементы в массиве, что происходит с данными / местоположениями всех оставшихся элементов?

Вам может не понравиться поведение по умолчанию в NumPy, что происходит, когда вы пытаетесь сделать «незаконное» назначение, нарушающее тип данных - возможно, вы хотите, чтобы вместо тихого усечения была выдана ошибка? Если это так, вы должны опубликовать об этом в списке NumPy, так как я думаю, что это более фундаментальная проблема, чем Tabular, с которой можно справиться - и независимо от наших личных чувств об обработке ошибок, мы бы хотели быть совместимыми с тем, что NumPy делает здесь.

Вам также может не понравиться, как Tabular делает вывод типа данных. На самом деле, NumPy остается в стороне от выводов dtype и в основном всегда требует от пользователя явного указания типов данных. Это хорошо в том смысле, что требует от пользователя обдумывать эти проблемы, но раздражает то, что иногда это довольно громоздко. Tabular пытается найти счастливую среду, которая полезна большую часть времени, но иногда это не удается - в этом случае значения по умолчанию можно переопределить, просто указав те же ключевые аргументы, что и конструкторы NumPy.

Я действительно думаю, что вы не совсем правы, когда говорите, что «подход, распространенный в базах данных 1974 года, ориентированных на записи, не может быть подходящим для Python в 2011 году». Фактически, основы управления памятью NumPy на самом деле являются теми же инструментами, которые использовались в 1970-х годах - это может быть удивительно, но большие части оптимизированного NumPy все еще построены на Fortran! Проблем с выделением памяти в те дни не всегда можно было избежать даже сегодня, хотя в большинстве случаев NumPy обеспечивает гораздо более чистый и простой интерфейс. Но нужно сказать, что если вы «с радостью попросите систему сделать небольшое копирование данных, чтобы упростить мой код», то, вероятно, NumPy и Tabular не для вас, поскольку автоматическое копирование данных и все, что они представляют, явно вразрез с замыслом дизайна этих пакетов.

Итак, возникает вопрос: какова ваша цель? Если вам действительно нужна производительность с операциями, подобными массиву, тогда используйте NumPy - в этом случае Tabular предоставляет операции, подобные электронным таблицам, - но в пределах ограничений NumPy. Если вам не нужна производительность, нет смысла начинать с объектов, подобных массиву, и вы можете быть более гибкими. Однако операции, подобные табличным, не распространяются на обычные объекты Python - и даже не совсем ясно, как сделать это расширение.

И, позвольте мне добавить еще одну (весьма важную) вещь - OP, если производительность не является вашей основной проблемой, но вы все еще хотите использовать Tabular в качестве источника операций с электронными таблицами, вы можете просто выполнить все операции, которые вы хочу, чтобы это могло изменить типы данных с новыми вызовами конструктора массива Tabular. То есть, если в данной операции вам может потребоваться назначить новый тип данных строки большего размера, просто каждый раз создайте новый Tabarray. Это явно не так хорошо для производительности, если это не ваше ограничение, то это не должно быть проблемой.

Ключевым моментом здесь является то, что Tabular и NumPy устанавливают определенные стандарты для того, что считается «быстрым» или «медленным», а затем вынуждают вас открыто говорить об операциях, которые будут медленными. Они никогда не позволяют вам скрывать (как, например, Matlab) очень медленные операции под капотом. Что-то, что легко синтаксически, должно быть быстрым - и если вы хотите сделать что-то, что будет медленным, вам придется немного потрудиться в своем коде, чтобы сделать это, и, следовательно, обратить внимание на то, что происходит. В результате ваш код становится чище и лучше, но все же легче писать, чем если бы вы работали непосредственно в C или Fortran. Фактически, этот принцип в значительной степени применим и ко всему самому Python - хотя и с несколько иными стандартами для того, что считается «быстрым» или «медленным».

НТН, D

2 голосов
/ 30 ноября 2011

Я не пытаюсь быть грубым здесь, но вы не понимаете, что такое numpy.

Вы не хотите, чтобы NumPy. Вы хотите dict.

Массивы Numpy являются , а не контейнерами данных общего назначения. Это эффективные для памяти контейнеры для унифицированных данных. Весь смысл в том, чтобы быть низкоуровневым контейнером данных, который может хранить вещи практически без перегрузок памяти (т. Е. 100 64-битных операций с плавающей запятой используют 800 байтов памяти в качестве массива с нулевыми значениями. список.)

Если вы хотите хранить неоднородные данные, не используйте пустой массив.

Python имеет очень богатый набор встроенных структур данных. В этом случае вам нужен диктант или список.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...