混合柱#

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")

您可以方便地转换 TableQTable 反之亦然:

>>> qt2 = QTable(t)
>>> type(qt2['velocity'])
<class 'astropy.units.quantity.Quantity'>

>>> t2 = Table(qt2)
>>> type(t2['velocity'])
<class 'astropy.table.column.Column'>

备注

总结一下: only 两者之间的区别 QTableTable 添加具有指定单位的列时的行为。与 QTable 这样的列总是转换为 Quantity 对象,然后添加到表中。同样,如果为现有单位指定了一个单位,则 Column 在一个 QTable ,然后将列转换为 Quantity .

反之,如果你加上 Quantity 一列普通的 Table 然后转换成普通的 Column 与对应的 unit 属性。

注意

当一列 int dtype 被转换为 Quantity ,ITS dtype 被转换为 float

例如,对于质量标志列 int ,如果将其分配给 dimensionless unit ,它仍将被转换为 float 。因此,这样的柱通常不应与任何单位一起分配。

混合属性#

常用的列属性 namedtypeunitformatdescription 可通过 info 属性:

>>> qt['velocity'].info.name
'velocity'

info 属性是一种关键的粘合剂,它允许非 Column 对象的行为类似于 Column

相同的 info 标准配置中也提供了属性 Column 物体。这些 infot['a'].info.name 请直接联系 Column 属性(例如。, t['a'].name )可以互换使用。同样的 Quantity 对象, info.dtype 属性引用本机 dtype 对象的属性。

备注

在编写处理可能是mixin列的列对象的通用代码时,必须 总是 使用 info 属性来访问列属性。

细节和注意事项#

当mixin列是表的一部分时,大多数常用表操作的行为与预期一致。然而,目前的实施存在局限性。

添加或插入行

添加或插入行只对可变(数据可以在内部更改)和具有 insert() 方法。将行添加到 Table 使用 QuantityTimeSkyCoord 柱子可以工作。

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``
    """

可以在 ColumnInfoQuantityInfo 类。

示例: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 要将该对象类视为类似于内置混合列的混合,请执行以下操作 TimeQuantity

对于在第三方包中定义且您无法控制的数据类,可以执行此操作。例如,我们定义了一个不同于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 例如。