混合柱#
astropy
表支持“混合列”的概念,它允许集成适当的非 Column
中基于类对象的 Table
对象。这些Mixin列对象不会以任何方式转换,而是在本地使用。
可用的内置mixin列类有:
基本示例#
例如,我们可以创建一个表并添加一个时间列:
>>> from astropy.table import Table
>>> from astropy.time import Time
>>> t = Table()
>>> t['index'] = [1, 2]
>>> t['time'] = Time(['2001-01-02T12:34:56', '2001-02-03T00:01:02'])
>>> print(t)
index time
----- -----------------------
1 2001-01-02T12:34:56.000
2 2001-02-03T00:01:02.000
重要的一点是 time
专栏是善意的 Time
对象:
>>> t['time']
<Time object: scale='utc' format='isot' value=['2001-01-02T12:34:56.000' '2001-02-03T00:01:02.000']>
>>> t['time'].mjd
array([51911.52425926, 51943.00071759])
数量和数量#
本机处理的能力 Quantity
表中的对象使以自然和健壮的方式操作带有单位的表格数据更加方便。但是,这个特性引入了一个模糊性,因为带有单位的数据(例如,来自FITS二进制表)可以表示为 Column
用一个 unit
属性或作为 Quantity
对象。为了彻底解决这种模糊性, astropy
定义 Table
类称为 QTable
. 这个 QTable
类与 Table
除了那个 Quantity
是具有已定义单位的任何数据列的默认值。
如果你利用 Quantity
那么你的分析中的基础设施 QTable
是使用单位创建表的首选方法。如果您更多地使用表列单位作为描述性标签,那么 Table
类可能是最好的类。
例子#
为了说明这些概念,我们首先创建一个标准 Table
在这里我们提供输入a Time
对象与A Quantity
对象的单位为 m / s
. 在这种情况下,数量转换为 Column
(它有一个 unit
属性,但不具有 Quantity
):
>>> import astropy.units as u
>>> t = Table()
>>> t['index'] = [1, 2]
>>> t['time'] = Time(['2001-01-02T12:34:56', '2001-02-03T00:01:02'])
>>> t['velocity'] = [3, 4] * u.m / u.s
>>> print(t)
index time velocity
m / s
----- ----------------------- --------
1 2001-01-02T12:34:56.000 3.0
2 2001-02-03T00:01:02.000 4.0
>>> type(t['velocity'])
<class 'astropy.table.column.Column'>
>>> t['velocity'].unit
Unit("m / s")
>>> (t['velocity'] ** 2).unit # WRONG because Column is not smart about unit
Unit("m / s")
因此,让我们使用一个 QTable
**
>>> from astropy.table import QTable
>>> qt = QTable()
>>> qt['index'] = [1, 2]
>>> qt['time'] = Time(['2001-01-02T12:34:56', '2001-02-03T00:01:02'])
>>> qt['velocity'] = [3, 4] * u.m / u.s
这个 velocity
列现在是 Quantity
并据此行事:
>>> type(qt['velocity'])
<class 'astropy.units.quantity.Quantity'>
>>> qt['velocity'].unit
Unit("m / s")
>>> (qt['velocity'] ** 2).unit # GOOD!
Unit("m2 / s2")
>>> qt2 = QTable(t)
>>> type(qt2['velocity'])
<class 'astropy.units.quantity.Quantity'>
>>> t2 = Table(qt2)
>>> type(t2['velocity'])
<class 'astropy.table.column.Column'>
备注
总结一下: only 两者之间的区别 QTable
和 Table
添加具有指定单位的列时的行为。与 QTable
这样的列总是转换为 Quantity
对象,然后添加到表中。同样,如果为现有单位指定了一个单位,则 Column
在一个 QTable
,然后将列转换为 Quantity
.
注意
当一列 int
dtype
被转换为 Quantity
,ITS dtype
被转换为 float
。
例如,对于质量标志列 int
,如果将其分配给 dimensionless unit ,它仍将被转换为 float
。因此,这样的柱通常不应与任何单位一起分配。
混合属性#
常用的列属性 name
, dtype
, unit
, format
和 description
可通过 info
属性:
>>> qt['velocity'].info.name
'velocity'
这 info
属性是一种关键的粘合剂,它允许非 Column
对象的行为类似于 Column
。
相同的 info
标准配置中也提供了属性 Column
物体。这些 info
像 t['a'].info.name
请直接联系 Column
属性(例如。, t['a'].name
)可以互换使用。同样的 Quantity
对象, info.dtype
属性引用本机 dtype
对象的属性。
备注
在编写处理可能是mixin列的列对象的通用代码时,必须 总是 使用 info
属性来访问列属性。
细节和注意事项#
当mixin列是表的一部分时,大多数常用表操作的行为与预期一致。然而,目前的实施存在局限性。
添加或插入行
添加或插入行只对可变(数据可以在内部更改)和具有 insert()
方法。将行添加到 Table
使用 Quantity
, Time
或 SkyCoord
柱子可以工作。
Masking
混合列的掩码是由 Masked
班级。看见 屏蔽值 (astropy.utils.masked ) 了解更多细节。
ASCII表写入
将具有MixIn列的表写出到文件中 astropy.io.ascii
模块,但基于C的快速编写器不可用。取而代之的是,将使用纯Python编写器。要编写具有MixIn列的表,建议使用 ECSV格式 。这将完全序列化表数据和元数据,从而允许在读回表时对其进行完整的“往返”。
二进制表写入
包含Mixin列的表格可以使用FITS、HDF5和PARQUET格式写入二进制文件。可以回读这些文件以精确地恢复原始文件 Table
包括混合列和元数据。看见 统一文件读写接口 了解更多细节。
Mixin协议#
mixin列背后的一个关键思想是,可以使用任何满足指定协议的类。这意味着许多处理类数组数据的用户定义类对象可以在 Table
. 协议相对简洁,并且要求类的行为类似于 numpy
具有以下属性的数组:
包含类似数组的数据。
机具
__getitem__()
支持以单项、切片或索引数组访问的形式获取数据。有一个
shape
属性。有一个
__len__()
长度的计算方法。有一个
info
类描述符,它是astropy.utils.data_info.MixinInfo
班级。
这个 Example: ArrayWrapper 一节显示了一个可以用作混合列的类的最小工作示例。一个 pandas.Series
对象也可以用作混合列。
mixin列的其他有趣的可能性包括:
作为其他列的函数动态计算的列(又称电子表格)。
列本身是
Table
(即嵌套表)。A proof of concept 可用。
new_like()方法#
为了支持像这样的高级操作 join()
和 vstack()
,则MixIn类必须提供 new_like()
方法中的 info
类描述符。该功能的一个关键部分是确保适当地合并输入列元数据,并确保列具有一致的属性,如形状。
提供以下功能的Mixin类 new_like()
还必须实施 __setitem__()
支持通过单个项、切片或索引数组进行设置。
这个 new_like()
方法具有以下签名:
def new_like(self, cols, length, metadata_conflicts='warn', name=None):
"""
Return a new instance of this class which is consistent with the
input ``cols`` and has ``length`` rows.
This is intended for creating an empty column object whose elements can
be set in-place for table operations like join or vstack.
Parameters
----------
cols : list
List of input columns
length : int
Length of the output column object
metadata_conflicts : {'warn', 'error', 'silent'}
How to handle metadata conflicts
name : str
Output column name
Returns
-------
col : object
New instance of this class consistent with ``cols``
"""
可以在 ColumnInfo
和 QuantityInfo
类。
示例:ArrayWrapper#
下面的代码清单显示了一个充当Mixin列类的数据容器类的示例。此类是一个包装,包装在 numpy.ndarray
。它用在 astropy
Mixin测试套件,完全符合作为Mixin柱的要求。
from astropy.utils.data_info import ParentDtypeInfo
class ArrayWrapper(object):
"""
Minimal mixin using a simple wrapper around a numpy array
"""
info = ParentDtypeInfo()
def __init__(self, data):
self.data = np.array(data)
if 'info' in getattr(data, '__dict__', ()):
self.info = data.info
def __getitem__(self, item):
if isinstance(item, (int, np.integer)):
out = self.data[item]
else:
out = self.__class__(self.data[item])
if 'info' in self.__dict__:
out.info = self.info
return out
def __setitem__(self, item, value):
self.data[item] = value
def __len__(self):
return len(self.data)
@property
def dtype(self):
return self.data.dtype
@property
def shape(self):
return self.data.shape
def __repr__(self):
return f"<{self.__class__.__name__} name='{self.info.name}' data={self.data}>"
将类似数组的对象注册为混合列#
在某些情况下,您可能希望直接将类似数组的对象添加为表列,同时保持原始对象属性(而不是将对象默认转换为 Column
)。这是通过将对象类注册为混合输入列并定义一个处理程序来实现的,该处理程序允许 Table
要将该对象类视为类似于内置混合列的混合,请执行以下操作 Time
或 Quantity
。
对于在第三方包中定义且您无法控制的数据类,可以执行此操作。例如,我们定义了一个不同于NumPy的类,并将数据存储在私有属性中::
>>> import numpy as np
>>> class ExampleDataClass:
... def __init__(self):
... self._data = np.array([0, 1, 3, 4], dtype=float)
默认情况下,这不能用作表列::
>>> t = Table()
>>> t['data'] = ExampleDataClass()
Traceback (most recent call last):
...
TypeError: Empty table cannot have column set to scalar value
但是,您可以创建一个函数(或‘处理程序’),该函数接受您希望自动处理的数据类的实例,并返回一个MixIn列::
>>> from astropy.table.table_helpers import ArrayWrapper
>>> def handle_example_data_class(obj):
... return ArrayWrapper(obj._data)
然后,您可以通过提供类和处理程序函数的完全限定名称来注册它::
>>> from astropy.table.mixins.registry import register_mixin_handler
>>> register_mixin_handler('__main__.ExampleDataClass', handle_example_data_class)
>>> t['data'] = ExampleDataClass()
>>> t
<Table length=4>
data
float64
-------
0.0
1.0
3.0
4.0
由于我们将数据类定义为上述示例的一部分,因此完全限定名称以 __main__
,但对于第三方包中的类,可能如下所示 package.Class
例如。