ECSV格式#
这个 Enhanced Character-Separated Values (ECSV) format 可以用来写 astropy
Table
或 QTable
数据集到纯文本的人类可读数据文件,然后读回表,而不会丢失信息。该格式使用YAML头数据结构存储诸如单位和数据类型之类的列规范以及表元数据。实际的表格数据以标准的字符分隔值(CSV)格式存储,从而与各种非专用的CSV表读取器兼容。
注意
建议使用ECSV格式将表格数据存储在人类可读的ASCII文件中。这包括从科学研究的非正式使用到生产管道和数据系统的用例。
用法#
当以ECSV格式编写时,分隔符只有两种选择,空格或逗号,空格是默认分隔符。任何其他价值的 delimiter
将给出一个错误。用于读取的分隔符是在文件本身中指定的。
除了分隔符之外,唯一适用的其他读/写参数是 names
, include_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.mask
和 y.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'
都会被保存下来。
混合柱#
不仅可以存储标准 Column
和 MaskedColumn
对象提交给ECSV,但也包括以下内容 混合柱 :
通常,mixin列可能包含多个数据组件以及超出标准的对象属性 Column
像 format
或 description
. 存储这样的mixin列是通过将mixin列替换为表示底层数据组件的列,然后插入元数据来通知读者如何重建原始列。例如,a SkyCoord
混合柱输入 'spherical'
表示将具有数据属性 ra
, dec
, distance
,以及对象属性,如 representation_type
或 frame
.
此示例演示如何编写 QTable
那就是 Time
和 SkyCoord
混合列::
>>> 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__'
关键字包含标准值 Column
像 description
或 format
,对于由多个序列化列表示的任何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