ECSV格式#

这个 Enhanced Character-Separated Values (ECSV) format 可以用来写 astropy TableQTable 数据集到纯文本的人类可读数据文件,然后读回表,而不会丢失信息。该格式使用YAML头数据结构存储诸如单位和数据类型之类的列规范以及表元数据。实际的表格数据以标准的字符分隔值(CSV)格式存储,从而与各种非专用的CSV表读取器兼容。

注意

建议使用ECSV格式将表格数据存储在人类可读的ASCII文件中。这包括从科学研究的非正式使用到生产管道和数据系统的用例。

除Python外,还支持ECSV TOPCAT 在Java中 STIL 类库。

用法#

当以ECSV格式编写时,分隔符只有两种选择,空格或逗号,空格是默认分隔符。任何其他价值的 delimiter 将给出一个错误。用于读取的分隔符是在文件本身中指定的。

除了分隔符之外,唯一适用的其他读/写参数是 namesinclude_names ,以及 exclude_names 。所有其他参数将被忽略或引发错误。

简易表#

下面将表编写为一个简单的空格分隔文件。由于以下原因,会自动选择ECSV格式 .ecsv 后缀::

>>> import numpy as np
>>> from astropy.table import Table
>>> data = Table()
>>> data['a'] = np.array([1, 2], dtype=np.int8)
>>> data['b'] = np.array([1, 2], dtype=np.float32)
>>> data['c'] = np.array(['hello', 'world'])
>>> data.write('my_data.ecsv')  

的内容 my_data.ecsv 如下所示:

# %ECSV 1.0
# ---
# datatype:
# - {name: a, datatype: int8}
# - {name: b, datatype: float32}
# - {name: c, datatype: string}
# schema: astropy-2.0
a b c
1 1.0 hello
2 2.0 world

ECSV标头是以 # 注释字符。ECSV文件必须以 %ECSV <version> 排队。这个 datatype 元素定义列的列表,而 schema 与用于编写代码的Astery特定扩展相关 Mixin Columns

屏蔽数据#

您可以通过两种不同的方式写入ECSV格式的屏蔽(或“缺失”)数据,一种是使用空字符串表示缺失的值,另一种是通过将屏蔽列拆分成单独的数据和屏蔽列。

空串#

第一种(默认)方式使用空字符串代替掩码值作为标记。这在国外更常见一些 astropy 并且不需要任何星象特定的扩展。

>>> from astropy.table import MaskedColumn
>>> t = Table()
>>> t['x'] = MaskedColumn([1.0, 2.0, 3.0], unit='m', dtype='float32')
>>> t['x'][1] = np.ma.masked
>>> t['y'] = MaskedColumn([False, True, False], dtype='bool')
>>> t['y'][0] = np.ma.masked
>>> t.write('my_data.ecsv', format='ascii.ecsv', overwrite=True)  

的内容 my_data.ecsv 如下所示:

# %ECSV 1.0
# ---
# datatype:
# - {name: x, unit: m, datatype: float32}
# - {name: y, datatype: bool}
# schema: astropy-2.0
x y
1.0 ""
"" True
3.0 False

要回读这段代码,您需要运行以下命令:

>>> Table.read('my_data.ecsv')  
<Table length=3>
   x      y
   m
float32  bool
------- -----
    1.0    --
     --  True
    3.0 False

数据+掩码#

第二种方法是告诉编写器将任何掩码列分为数据列和掩码列,方法是提供 serialize_method='data_mask' 论点::

>>> t.write('my_data.ecsv', serialize_method='data_mask', overwrite=True)  

您可能希望执行此操作的主要原因有两个:

  • “在掩码下”存储数据,而不是用空字符串替换它。

  • 写入包含未屏蔽的空字符串的字符串列。

的内容 my_data.ecsv 如下所示。首先,请注意有两个新列 x.masky.mask ,并且它们显式地记录这些列的掩码值。接下来请注意,ECSV标头稍微复杂一些,它包含特定于Astery的扩展,这些扩展告诉读者如何解释普通的CSV列 x, x.mask, y, y.mask 并将它们重新组装到适当的屏蔽柱中。**

# %ECSV 1.0
# ---
# datatype:
# - {name: x, unit: m, datatype: float32}
# - {name: x.mask, datatype: bool}
# - {name: y, datatype: bool}
# - {name: y.mask, datatype: bool}
# meta: !!omap
# - __serialized_columns__:
#     x:
#       __class__: astropy.table.column.MaskedColumn
#       data: !astropy.table.SerializedColumn {name: x}
#       mask: !astropy.table.SerializedColumn {name: x.mask}
#     y:
#       __class__: astropy.table.column.MaskedColumn
#       data: !astropy.table.SerializedColumn {name: y}
#       mask: !astropy.table.SerializedColumn {name: y.mask}
# schema: astropy-2.0
x x.mask y y.mask
1.0 False False True
2.0 True True False
3.0 False False False

备注

对于有安全意识的人来说, __class__ 值必须在读取器信任的Aspy类的允许列表内。您不能在这里使用任意类。

按列控制#

在极少数情况下,可能需要分别为每列指定序列化方法。这一点如下例所示:

>>> from astropy.table.table_helpers import simple_table
>>> t = simple_table(masked=True)
>>> t['c'][0] = ""  # Valid empty string in data
>>> t
<Table masked=True length=3>
  a      b     c
int64 float64 str1
----- ------- ----
   --     1.0
    2     2.0   --
    3      --    e

现在,我们告诉ECSV编写器为字符串列输出单独的数据列和掩码列 'c'

>>> t['c'].info.serialize_method['ecsv'] = 'data_mask'
>>> ascii.write(t, format='ecsv')
# %ECSV 1.0
# ---
# datatype:
# - {name: a, datatype: int64}
# - {name: b, datatype: float64}
# - {name: c, datatype: string}
# - {name: c.mask, datatype: bool}
# meta: !!omap
# - __serialized_columns__:
#     c:
#       __class__: astropy.table.column.MaskedColumn
#       data: !astropy.table.SerializedColumn {name: c}
#       mask: !astropy.table.SerializedColumn {name: c.mask}
# schema: astropy-2.0
a b c c.mask
"" 1.0 "" False
2 2.0 d True
3 "" e False

当您读回这段代码时,空(零长度)字符串和掩码的 'd' 列中的值 'c' 都会被保存下来。

混合柱#

不仅可以存储标准 ColumnMaskedColumn 对象提交给ECSV,但也包括以下内容 混合柱

通常,mixin列可能包含多个数据组件以及超出标准的对象属性 Columnformatdescription . 存储这样的mixin列是通过将mixin列替换为表示底层数据组件的列,然后插入元数据来通知读者如何重建原始列。例如,a SkyCoord 混合柱输入 'spherical' 表示将具有数据属性 radecdistance ,以及对象属性,如 representation_typeframe .

此示例演示如何编写 QTable 那就是 TimeSkyCoord 混合列::

>>> from astropy.coordinates import SkyCoord
>>> import astropy.units as u
>>> from astropy.table import QTable

>>> sc = SkyCoord(ra=[1, 2] * u.deg, dec=[3, 4] * u.deg)
>>> sc.info.description = 'flying circus'
>>> q = [1, 2] * u.m
>>> q.info.format = '.2f'
>>> t = QTable()
>>> t['c'] = [1, 2]
>>> t['q'] = q
>>> t['sc'] = sc

>>> t.write('my_data.ecsv')  

的内容 my_data.ecsv 详情如下:

# %ECSV 1.0
# ---
# datatype:
# - {name: c, datatype: int64}
# - {name: q, unit: m, datatype: float64, format: .2f}
# - {name: sc.ra, unit: deg, datatype: float64}
# - {name: sc.dec, unit: deg, datatype: float64}
# meta: !!omap
# - __serialized_columns__:
#     q:
#       __class__: astropy.units.quantity.Quantity
#       __info__: {format: .2f}
#       unit: !astropy.units.Unit {unit: m}
#       value: !astropy.table.SerializedColumn {name: q}
#     sc:
#       __class__: astropy.coordinates.sky_coordinate.SkyCoord
#       __info__: {description: flying circus}
#       dec: !astropy.table.SerializedColumn
#         __class__: astropy.coordinates.angles.Latitude
#         unit: &id001 !astropy.units.Unit {unit: deg}
#         value: !astropy.table.SerializedColumn {name: sc.dec}
#       frame: icrs
#       ra: !astropy.table.SerializedColumn
#         __class__: astropy.coordinates.angles.Longitude
#         unit: *id001
#         value: !astropy.table.SerializedColumn {name: sc.ra}
#         wrap_angle: !astropy.coordinates.Angle
#           unit: *id001
#           value: 360.0
#       representation_type: spherical
# schema: astropy-2.0
c q sc.ra sc.dec
1 1.0 1.0 3.0
2 2.0 2.0 4.0

这个 '__class__' 关键字提供完全限定的类名,并且必须是指定允许的 astropy 类。没有添加用户指定的允许类的选项。这个 '__info__' 关键字包含标准值 Columndescriptionformat ,对于由多个序列化列表示的任何mixin列。

多维列#

使用ECSV可以编写包含多维列(掩码和非掩码)的表。这是通过使用JSON将每个元素编码为字符串来实现的。此功能适用于ECSV支持的所有列类型,包括 混合柱 。此功能是在Astopy4.3和ECSV版本1.0中添加的。

我们首先定义一个包含2行的表,其中第二列中的每个元素 'b' 本身是一个3x2数组::

>>> t = Table()
>>> t['a'] = ['x', 'y']
>>> t['b'] = np.arange(12, dtype=np.float64).reshape(2, 3, 2)
>>> t
<Table length=2>
 a        b
str1 float64[3,2]
---- ------------
   x   0.0 .. 5.0
   y  6.0 .. 11.0

>>> t['b'][0]
array([[0., 1.],
      [2., 3.],
      [4., 5.]])

现在我们可以将其写入ECSV,并观察N-d列 'b' 已被写为字符串,带有 datatype: string 。另请注意,列的列描述符包括新的 subtype: float64[3,2] 指定每一项的类型和形状的属性。

>>> ascii.write(t, format='ecsv')  
# %ECSV 1.0
# ---
# datatype:
# - {name: a, datatype: string}
# - {name: b, datatype: string, subtype: 'float64[3,2]'}
# schema: astropy-2.0
a b
x [[0.0,1.0],[2.0,3.0],[4.0,5.0]]
y [[6.0,7.0],[8.0,9.0],[10.0,11.0]]

当您读回它时,然后使用JSON将JSON编码的列项序列解码回原始的N-d列。

可变长度数组#

当每个数组元素的长度可能不同时,ECSV支持存储多维列。中支持此数据结构 FITS standard 。而当 numpy 本身不支持可变长度数组,可以使用类型为 np.ndarray 物体。这就是为什么 astropy Fits读取器输出可变长度的数组。

此功能是在Astopy4.3和ECSV版本1.0中添加的。

最常见的可变长度阵列在列的每个单元中具有一维阵列。您可以使用1-d作为列 np.ndarray 长度分别为2、5和3的单元格。

ECSV标准和 astropy 还支持每个单元格中的任意N-d数组,其中除最后一个维度外的所有维度都必须匹配。例如,您可以有一个列,其中 np.ndarray 具有以下形状的单元 (4,4,2)(4,4,5) ,以及 (4,4,3) 分别进行了分析。

下面的示例显示了将可变长度的一维数组写入ECSV。请注意新的ECSV列属性 subtype: 'int64[null]' 。这个 [null] 表示一个维度的可变长度。如果我们编写的是上面的N-d示例,子类型应该是 int64[4,4,null]

>>> t = Table()
>>> t['a'] = np.empty(3, dtype=object)
>>> t['a'] = [np.array([1, 2], dtype=np.int64),
...           np.array([3, 4, 5], dtype=np.int64),
...           np.array([6, 7, 8, 9], dtype=np.int64)]
>>> ascii.write(t, format='ecsv')
# %ECSV 1.0
# ---
# datatype:
# - {name: a, datatype: string, subtype: 'int64[null]'}
# schema: astropy-2.0
a
[1,2]
[3,4,5]
[6,7,8,9]

对象数组#

ECSV可以使用简单的Python对象存储对象类型的列,这些对象包括 dict, list, str, int, float, bool and None elements. More precisely, any object that can be serialized to JSON 使用标准库 json 支持套餐。

下面的示例显示了将对象数组写入ECSV。因为JSON需要用双引号将字符串括起来,而且ECSV需要 "" 要表示字符串中的双引号,在此表示中往往会出现双引号。

>>> t = Table()
>>> t['a'] = np.array([{'a': 1},
...                    {'b': [2.5, None]},
...                    True], dtype=object)
>>> ascii.write(t, format='ecsv')
# %ECSV 1.0
# ---
# datatype:
# - {name: a, datatype: string, subtype: json}
# schema: astropy-2.0
a
"{""a"":1}"
"{""b"":[2.5,null]}"
true