混合柱

astropy 表支持表中“mixin列”的概念,它允许集成适当的- 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 属性。

混合属性

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

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

这个 info 属性是一个关键的粘合剂,它允许非列对象的行为非常类似于列。

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

备注

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

细节和注意事项

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

添加或插入行

添加或插入行仅对可变(数据可以在内部更改)且具有 insert() 方法。 QuantityTime 支持 insert() 但是,例如, SkyCoord 没有。如果试图将行插入到具有 SkyCoord 列,则会发生如下异常:

ValueError: Unable to insert row because of exception in column 'skycoord':
'SkyCoord' object has no attribute 'insert'

从行列表或dict列表初始化

这种初始化表的模式不适用于mixin列,因此以下两种情况都将失败:

>>> qt = QTable([{'a': 1 * u.m, 'b': 2},
...              {'a': 2 * u.m, 'b': 3}])  
Traceback (most recent call last):
 ...
TypeError: only dimensionless scalar quantities can be converted to Python scalars

>>> qt = QTable(rows=[[1 * u.m, 2],
...                   [2 * u.m, 3]])  
Traceback (most recent call last):
 ...
TypeError: only dimensionless scalar quantities can be converted to Python scalars

问题在于知道是否以及如何将每个列的各个元素组合成适当的mixin列。当前代码使用 numpy 但不喜欢在字符串类型中对它执行mixor函数 QuantitySkyCoord .

Masking

Mixin列通常不支持屏蔽(除了 Time ),但对在屏蔽表中使用mixin的支持有限。在这种情况下a mask 属性指定给mixin列对象。这个 mask 是一个特殊对象,它是 False 对应于mixin数据形状。这个 mask 看起来很正常 numpy 数组,但如果 True 分配给任何元素。限制的后果在高级表操作中最为明显。

High-level table operations

下表总结了对包含mixin列的表的高级操作的支持:

操作

支持

分组操作

尚未实施,但没有根本限制。

垂直堆叠

可用于 Quantity subclasses, Time and any other mixin classes that provide a new_like() methodinfo 描述符。

水平堆叠

当输出混合列支持屏蔽或不需要屏蔽时工作。

加入

如果输出mixin列支持掩码或不需要掩码,则有效;键列必须是 numpy.ndarray .

唯一行

尚未实现,使用分组操作。

ASCII表写入

具有mixin列的表可以使用 astropy.io.ascii 模块,但快速的基于C的编写器不可用。相反,将使用纯Python编写器。对于使用mixin列编写表,建议使用 'ecsv' ASCII格式。这将完全序列化表数据和元数据,允许在读回表时进行完整的“往返”。看到了吗 ECSV格式 有关详细信息。

二进制表写入

`astropy 3.0中,包含mixin列的表可以使用FITS和HDF5格式以二进制格式写入文件。这些可以读回原稿 Table 包括mixin列和元数据。看到了吗 统一文件读写接口 有关详细信息。

Mixin协议

mixin列背后的一个关键思想是,可以使用任何满足指定协议的类。这意味着许多处理类数组数据的用户定义类对象可以在 Table . 协议相对简洁,并且要求类的行为类似于 numpy 具有以下属性的数组:

  • 包含类似数组的数据。

  • 器具 __getitem__ 支持将数据作为单个项、切片或索引数组访问获取。

  • 有一个 shape 属性。

  • 有一个 __len__ 长度的方法。

  • 有一个 info 类描述符,它是 astropy.utils.data_info.MixinInfo 班级。

这个 Example: ArrayWrapper 节显示了一个可以用作mixin列的类的最小工作示例。A pandas.Series 对象也可以用作mixin列。

mixin列的其他有趣的可能性包括:

  • 作为其他列的函数动态计算的列(又称电子表格)。

  • 列本身是 Table (即嵌套表)。A proof of concept 可用。

new_like()方法

为了支持像 joinvstack ,mixin类必须提供 new_like() 方法在 info 类描述符。该功能的一个关键部分是确保适当地合并输入列元数据,并且列具有一致的属性,例如形状。

提供 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 : str ('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 数组。它用于 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}>"