更改tabular.tabarray或numpy.recarray的数据类型(dtype)

Jon*_*ice 1 python numpy

我希望将数据表示为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')])
Run Code Online (Sandbox Code Playgroud)

嗯... tabarray!你在那里截断我的字符串!真?!NumPy dtype'| S5'表示字符串为5个或更少字符,但是加油!更新dtype.如果需要,重新格式化整个列.随你.但是不要默默地扔掉我的数据!

我尝试了其他几种方法,其中没有一种可以解决问题.例如,它在tabarray创建时直观了数据类型/大小,但在添加记录时却没有:

>>> 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')])
Run Code Online (Sandbox Code Playgroud)

我尝试切出整个列,更改其类型,设置值并重新分配:

>>> 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')])
>>> 
Run Code Online (Sandbox Code Playgroud)

它正确地进行了值赋值,但原始数据类型仍然有效,并且我以前正确的数据再次被静默截断.我甚至尝试过显式数据类型设置:

>>> 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')])
Run Code Online (Sandbox Code Playgroud)

好主!那更糟!它正确映射intfloat类型,但它猜测这str意味着我想要0长度的字符串,并将所有数据截断为空.长话短说,不仅表格不像开箱即用的电子表格,我找不到让它工作的方法.对我来说,性能不是一个大问题.我的电子表格可能有数百或数千行,最多,我很乐意让系统进行一些数据复制,以使我的代码更容易.Tabular似乎在许多其他方面非常适合该法案.

我想我可以使用默认情况下将所有字符串默认为不可思议的大字段(例如1024或4096字节)的子表格,并使用一种__setitem__方法在应该分配更大的字符串时引发异常.相当邋...... ......但还有更好的选择吗?我扎根于numpy.recarray等等,并没有看到明确的方式......但我会第一个承认我完全不熟悉NumPy.实际情况是,数据操作程序可能会增加字符串的长度超过其初始最大值.当然,高功能模块应该适应这种情况."只是截断它!" 1974年面向记录的数据库中常见的方法在2011年不适合Python的最新技术!

想法和建议?

Dan*_*Dan 6

作为表格的设计者之一...我不得不说,我在很大程度上认为第一个回答者的问题就是钉在头上.

OP,你谴责的"截断"行为是NumPy的一个基本问题,Tabular是基于它的.但是说它是一个应该修复的"错误"并不是真的准确,它更像是一个"限制",它与NumPy(和Tabular)的全部观点相呼应.

正如第一个回答者所指出的那样,NumPy绝对要求数据结构的大小一致.一旦分配了给定数据类型的numpy数组,该数组必须保持该数据类型 - 否则,必须初始化具有新内存的新数组.对于字符串数据类型,字符串的长度是数据类型的固定部分 - 您不能将长度为N的字符串数组"转换"为长度为M的字符串数组.

固定数据表对于NumPy比标准Python对象获得巨大性能提升的方式至关重要.这是因为,对于固定数据类型,NumPy对象知道已经为每个对象分配了多少字节,并且可以在内存空间中"跳转"到给定条目"应该"的位置,而无需读取和处理该内容.与Python列表不同,所有插入条目.当然,这限制了自然是n numpy数组的对象类型......或者实际上,它限制了可以对numpy数组执行的操作类型.与完全可变的Python列表不同(例如,您可以用任何其他python对象替换任何元素,而不会干扰列表中所有其他对象的内存分配),您不能将numpy数组的值变为对象的不同的数据类型 - 因为字节会计如何工作呢?如果突然第N个项目比数组中的所有其他项目大,那么所有剩余项目的数据/位置会发生什么变化?

您可能不喜欢NumPy的默认行为,当您尝试进行打破数据类型的"非法"分配时会发生什么 - 也许您希望发出错误而不是静默截断?如果是这样,你应该在NumPy列表上发布这个,因为我认为它比Tabular可以处理的问题更为根本 - 而且无论我们对错误处理的个人感受如何,我们都希望与NumPy在这里做的事情保持一致.

您可能也不喜欢Tabular如何进行数据类型推断.事实上,NumPy远离dtype推断,并且基本上总是要求用户明确指定数据类型.这是好的,因为它要求用户考虑这些问题,但它很烦人,因为它有时很麻烦.Tabular尝试点击大多数时候都很有用的快乐媒体,但有时这会失败 - 在这种情况下,只需指定与NumPy构造函数相同的关键字参数就可以覆盖默认值.

当你说"1974年面向记录的数据库中常见的方法不能成为2011年Python的最佳技术水平"时,我认为你不太对劲.事实上,NumPy内存管理的基础确实与20世纪70年代使用的工具完全相同 - 它可能会令人惊讶,但是大量优化的NumPy仍然建立在Fortran上!那些日子的内存分配问题即使在今天也无法避免,尽管NumPy在大多数时候确实提供了更清洁,更简单的界面.但必须要说的是,如果您"乐意让系统进行一些数据复制以使我的代码变得简单" - 那么可能NumPy和Tabular不适合您,因为静默数据复制及其代表的所有内容都是明确的违背这些包的设计意图.

所以问题就变成了:你的目标是什么?如果你真的需要使用类似数组的操作,而不是使用NumPy - 在这种情况下,Tabular提供类似操作的电子表格 - 但是仍然存在于NumPy的限制之内.如果您不需要性能,那么开始使用类似数组的对象是没有意义的,您可以更灵活.但是,Tabular的类似电子表格的操作并没有扩展到一般的python对象 - 而且甚至不清楚如何进行扩展.

而且,让我再添加一个(非常重要的)OP,如果性能不是您的主要问题,但您仍然希望使用Tabular作为电子表格操作的来源,您可以执行所需的所有操作使用对Tabular数组构造函数的新调用更改数据类型.也就是说,如果在给定的操作中,您可能需要对新的更大的字符串数据类型进行赋值,那么每次只需构造一个新的Tabarray.这显然不如性能好,如果这不是你的限制,那么应该没问题.

这里的关键点是Tabular和NumPy为"快"或"慢"设置了某些标准 - 然后,迫使你明确表示将会变慢的操作.它们从不允许你隐藏(例如Matlab的方式)在引擎盖下非常慢的操作.语法很容易的东西应该很快 - 如果你想做一些会变慢的事情,你应该在你的代码中更加努力地去做,因此要注意发生的事情.因此,您的代码最终变得更干净,更好,但仍然比直接在C或Fortran中工作更容易编写.事实上,这个原则在很大程度上也适用于所有Python本身 - 尽管对于"快"或"慢"的标准有些不同.

HTH,D