首页
/ scientific-agent-skills 项目实战指南:精通 Astropy 表格数据操作(astropy.table)全解析

scientific-agent-skills 项目实战指南:精通 Astropy 表格数据操作(astropy.table)全解析

2026-09-08 19:42:13作者:齐添朝

导读

astropy.table 是 Python 天文/天体物理生态中处理星表、观测记录、谱线数据等表格数据的核心模块。它融合了 NumPy 的高性能数组操作、数据库风格的 join/group 操作、物理单位(units)感知与掩码(masked value)缺失数据处理,并原生支持 FITS、HDF5、VOTable、CSV/ECSV/IPAC、Parquet、ASDF 等多种格式的读写。本仓库的 astropy skill(见 SKILL.md)面向需要 Astropy API 的天文分析工作流,本文基于其配套参考文档 tables.md 展开,结合仓库内 skill 定义与依赖清单,系统讲解表的构建、访问、修改、排序筛选、I/O、join/group、单位列、掩码、索引与性能优化,帮助你直接用可运行的代码完成从"读入星表"到"交叉匹配、分箱统计"的完整数据链路。


一、认识 astropy.table:为什么天文学分析需要它

从源码结构看,astropy skill 将核心能力拆解为 7 份主题参考文档:unitscoordinatescosmologyfitstablestime 以及 wcs_and_other_modules,分别对应 skills/astropy/references/ 下的同名文件。astropy.table 的价值在于它不是又一个 DataFrame 克隆,而是为天文数据量身定制:

  • 单位感知:列可以携带物理单位,结合 QTable 在计算中自动保持量纲一致;
  • 掩码支持:原生处理缺失/坏点数据,统计运算自动跳过掩码;
  • 格式互通:一条 Table.read / Table.write 即可桥接 FITS、CSV、VOTable、HDF5 等天文与通用格式;
  • 与生态融合:可无缝往返于 NumPy 数组与 pandas DataFrame。仓库的测试依赖清单 tests/skill-requirements.toml 中为 [skills.astropy] 声明了 astropymatplotlibnumpypandasdask,印证了该 skill 面向"pandas + 大规模数组(dask)"的扩展工作流。

二、创建表格:六种入口一次掌握

2.1 从列数组创建(最常用)

from astropy.table import Table, QTable
import astropy.units as u
import numpy as np

# 从列数组创建
a = [1, 4, 5]
b = [2.0, 5.0, 8.2]
c = ['x', 'y', 'z']

t = Table([a, b, c], names=('id', 'flux', 'name'))

# 带单位的列:使用 QTable
flux = [1.2, 2.3, 3.4] * u.Jy
wavelength = [500, 600, 700] * u.nm
t = QTable([flux, wavelength], names=('flux', 'wavelength'))

传列数组时列元素长度必须一致;names 可选,缺省时自动生成 col0, col1, ...。凡涉及物理单位(Jy、nm、deg、pc 等)建议直接使用 QTable,这正是 SKILL.md 中 Best Practice 第 8 条"Use QTable for unit-aware tables"的依据。

2.2 从行列表创建

# 行 = 元组
rows = [(1, 10.5, 'A'), (2, 11.2, 'B'), (3, 12.3, 'C')]
t = Table(rows=rows, names=('id', 'value', 'name'))

# 行 = 字典(键即列名)
rows = [{'id': 1, 'value': 10.5}, {'id': 2, 'value': 11.2}]
t = Table(rows)

2.3 从 NumPy 数组创建

# 结构化数组(dtype 自带列名与类型)
arr = np.array([(1, 2.0, 'x'), (4, 5.0, 'y')],
               dtype=[('a', 'i4'), ('b', 'f8'), ('c', 'U10')])
t = Table(arr)

# 2D 数组 + 显式列名
data = np.random.random((100, 3))
t = Table(data, names=['col1', 'col2', 'col3'])

2.4 从 pandas DataFrame 创建

import pandas as pd

df = pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6]})
t = Table.from_pandas(df)

反向转换使用 t.to_pandas()。由于 skill 测试环境声明了 pandas(见 tests/skill-requirements.toml),Astropy 表与 DataFrame 的互转是本 skill 日常分析中的标准桥接动作。


三、访问表格数据

3.1 列 / 行 / 单元格三级访问

# 列访问:返回 Column 对象
ra_col = t['ra']           # Returns Column object
dec_col = t['dec']

# 行访问:单行返回 Row 对象,切片返回新 Table
first_row = t[0]           # Returns Row object
row_slice = t[10:20]       # Returns new Table

# 单元格访问(两种等价写法)
value = t['ra'][5]         # 'ra' 列的第 6 个值
value = t[5]['ra']         # 同上

# 一次取多列
subset = t['ra', 'dec', 'mag']

3.2 表级属性与遍历

len(t)              # 行数
t.colnames          # 列名列表
t.dtype             # 各列数据类型
t.info              # 详细信息(含单位、格式、描述)
t.meta              # 元数据字典
# 遍历行
for row in t:
    print(row['ra'], row['dec'])

# 遍历列
for colname in t.colnames:
    print(t[colname])

四、修改表格:增删列、重命名、加行与改值

4.1 添加列

# 直接赋新列
t['new_col'] = [1, 2, 3, 4, 5]
t['calc'] = t['a'] + t['b']  # 由已有列计算出的新列

# 带单位的列
t['velocity'] = [10, 20, 30] * u.km / u.s

# 添加空列(可指定长度与 dtype)
from astropy.table import Column
t['empty'] = Column(length=len(t), dtype=float)

# 插入到指定位置(index 为插入后的位置)
t.add_column([7, 8, 9], name='inserted', index=2)

4.2 删除列

t.remove_column('old_col')          # 删除单列
t.remove_columns(['col1', 'col2'])  # 删除多列
del t['col_name']                   # 等价的 del 语法
t.keep_columns(['ra', 'dec', 'mag'])  # 只保留指定列

4.3 重命名列

t.rename_column('old_name', 'new_name')                 # 重命名单列
t.rename_columns(['old1', 'old2'], ['new1', 'new2'])    # 批量重命名

4.4 添加与修改数据

# 添加单行:按列序传列表,或按字典传键值
t.add_row([1, 2.5, 'new'])
t.add_row({'ra': 10.5, 'dec': 41.2, 'mag': 18.5})

# 注意:逐行 add_row 很慢!
# 更优做法:先收集行,再一次建表(见第八节性能优化)
# 修改列(整体替换/条件赋值)
t['flux'] = t['flux'] * gain
t['mag'][t['mag'] < 0] = np.nan

# 修改单个单元格
t['ra'][5] = 10.5

# 整行替换
t[0] = [new_id, new_ra, new_dec]

五、排序与筛选

5.1 排序

t.sort('mag')              # 单列升序
t.sort('mag', reverse=True)  # 降序
t.sort(['priority', 'mag'])  # 多列依次排序

# 不修改原表:拿到排序索引再切片
indices = t.argsort('mag')
sorted_table = t[indices]

5.2 布尔索引筛选(与 NumPy 习惯一致)

bright = t[t['mag'] < 18]                 # 亮于 18 等的源
nearby = t[t['distance'] < 100*u.pc]      # 距离 100 pc 内(单位比较自动成立)

# 多条件组合(必须加括号)
selected = t[(t['mag'] < 18) & (t['dec'] > 0)]

# 借助 numpy 函数做信噪比筛选
high_snr = t[np.abs(t['flux'] / t['error']) > 5]

布尔数组本质上是 NumPy 索引的扩展,因此 &(与)、|(或)、~(非)等位运算也可直接使用。


六、文件的读写

6.1 支持的格式

tables.md 明确列出:FITS、HDF5、ASCII(CSV、ECSV、IPAC 等)、VOTable、Parquet、ASDF。多样的格式支持正是 SKILL.md 中"table operations(reading catalogs, cross-matching, filtering, joining)"工作流的底层保障。

6.2 读取

# 自动探测格式(按扩展名)
t = Table.read('catalog.fits')
t = Table.read('data.csv')
t = Table.read('table.vot')

# 显式指定格式
t = Table.read('data.txt', format='ascii')
t = Table.read('catalog.hdf5', path='/dataset/table')

# 读取 FITS 指定 HDU
t = Table.read('file.fits', hdu=2)

FITS 是多 HDU(Header Data Unit)文件,hdu 参数用于定位存放表格的扩展;若表格在扩展 2,可直接用 hdu=2 跳开前导扩展。

6.3 写出

# 由扩展名自动推断格式
t.write('output.fits')
t.write('output.csv')

# 显式格式 / 指定 HDF5 路径 / 序列化元数据
t.write('output.txt', format='ascii.csv')
t.write('output.hdf5', path='/data/table', serialize_meta=True)

# 覆盖已存在文件
t.write('output.fits', overwrite=True)

serialize_meta=True 会把表元数据与列描述等一并写入 HDF5;overwrite=True 在目标已存在时避免抛错(Astropy 默认拒绝覆盖)。

6.4 ASCII 格式的细分选项

# 自定义分隔符的 CSV
t.write('output.csv', format='ascii.csv', delimiter='|')

# 定宽格式
t.write('output.txt', format='ascii.fixed_width')

# IPAC 格式(红外天文数据中心标准)
t.write('output.tbl', format='ascii.ipac')

# LaTeX 表(直接用于论文排版)
t.write('table.tex', format='ascii.latex')

七、表的组合运算:stack、join、group 与去重

7.1 纵向堆叠 vstack 与横向拼接 hstack

from astropy.table import vstack

# 上下拼接(列结构需一致)
t1 = Table([[1, 2], [3, 4]], names=('a', 'b'))
t2 = Table([[5, 6], [7, 8]], names=('a', 'b'))
t_combined = vstack([t1, t2])
from astropy.table import hstack

# 左右拼接(行数需一致)
t1 = Table([[1, 2]], names=['a'])
t2 = Table([[3, 4]], names=['b'])
t_combined = hstack([t1, t2])

7.2 数据库式 join

from astropy.table import join

# 在公共列 'id' 上内连接
t1 = Table([[1, 2, 3], ['a', 'b', 'c']], names=('id', 'data1'))
t2 = Table([[1, 2, 4], ['x', 'y', 'z']], names=('id', 'data2'))
t_joined = join(t1, t2, keys='id')

# left / right / outer 连接类型
t_joined = join(t1, t2, join_type='left')
t_joined = join(t1, t2, join_type='outer')

join_type 的取值语义与 SQL 完全对齐:'inner'(默认,仅保留匹配键)、'left'(保留左侧全部行,右侧缺失为掩码/NaN)、'outer'(保留两侧全部行)。

7.3 分组与聚合

# 按列分组(如按滤光片 filter)
g = t.group_by('filter')

# 对每组聚合:返回以 group key + 聚合结果为列的新表
means = g.groups.aggregate(np.mean)

# 逐组迭代
for group in g.groups:
    print(f"Filter: {group['filter'][0]}")
    print(f"Mean mag: {np.mean(group['mag'])}")

aggregate 接受任意可作用于数组并返回标量的函数(np.meannp.medianlen 等),非常适合做按测光波段统计亮度等常规任务。

7.4 唯一行

t_unique = t.unique('id')          # 按单列去重
t_unique = t.unique(['ra', 'dec']) # 按多列组合去重(如重复天体坐标)

八、单位感知:QTable 与等价换算

使用 QTable(而非 Table)即可让列携带物理单位,并在运算中自动校验与换算量纲:

from astropy.table import QTable

t = QTable()
t['flux'] = [1.2, 2.3, 3.4] * u.Jy
t['wavelength'] = [500, 600, 700] * u.nm

# 单位换算
t['flux'].to(u.mJy)
t['wavelength'].to(u.angstrom)

# 计算自动保持单位;spectral() 等价表实现波长↔频率互换
t['freq'] = t['wavelength'].to(u.Hz, equivalencies=u.spectral())

equivalencies=u.spectral() 是天文的典型用法——波长(nm/angstrom)与频率(Hz)本属不同量纲,借助光谱等价表(equivalency)才可无缝互换。单位换算体系的细节可进一步参考本 skill 的 references/units.md


九、缺失数据与掩码:MaskedColumn

天文观测中的非探测、坏像素常产生缺失值。掩码列可区分"数值本身"与"数据是否有效":

from astropy.table import MaskedColumn

# 显式指定 mask:第二元素被掩(缺失)
flux = MaskedColumn([1.2, np.nan, 3.4], mask=[False, True, False])
t = Table([flux], names=['flux'])

# 统计运算自动跳过被掩元素
mean_flux = np.ma.mean(t['flux'])

# 将掩码值替换为指定值(如 0)
t['flux'].filled(0)  # Replace masked with 0

需要注意:掩码列基于 numpy.ma 实现,因此统计时应使用 np.ma.* 系列函数(np.ma.mean 等)以正确处理掩码。


十、索引:加速大规模星表检索

当表很大、需要按主键反复查询时,为列建索引可把查找从线性扫描提升为索引检索:

# 为列建索引
t.add_index('id')

# 按索引值快速定位行
row = t.loc[12345]  # Find row where id=12345

# 范围查询
subset = t.loc[100:200]

# 多个索引并存时显式指定:优先用 'name' 索引查找
# 注意:t.loc["b", 2](首元素当索引名)自 7.2 起弃用,将于 9.0 移除
t.add_index('name')
row = t.loc.with_index('name')['M31']

SKILL.md 的版本说明可知,将表索引标识符作为 .loc 首元素传入的写法(t.loc["b", 2])自 Astropy 7.2 起已弃用,应改用 t.loc.with_index("b")[2];新代码请避免旧式写法。


十一、元数据管理

表级与列级元数据都是普通字典,用于保存观测上下文,并可在文件写出时序列化:

# 表级元数据
t.meta['TELESCOPE'] = 'HST'
t.meta['FILTER'] = 'F814W'
t.meta['EXPTIME'] = 300.0

# 列级元数据
t['ra'].meta['unit'] = 'deg'
t['ra'].meta['description'] = 'Right Ascension'
t['ra'].description = 'Right Ascension'  # 便捷属性写法

SKILL.md 的 FITS 工作流中,header['EXPTIME']header['FILTER'] 这类从文件头读取观测参数的场景与此处的 meta 写入形成互补:前者读 FITS 头,后者随表写入文件(需 serialize_meta=True)。


十二、性能优化:三个高频实践

12.1 批量构造胜过逐行 add_row

# SLOW:逐行 add_row(每次调用都有额外开销)
t = Table(names=['a', 'b'])
for i in range(1000):
    t.add_row([i, i**2])

# FAST:先构造行列表,一次性建表
rows = [(i, i**2) for i in range(1000)]
t = Table(rows=rows, names=['a', 'b'])

tables.md 特别标注了这一点——add_row 仅适合少量追加,批量数据务必"收集后统一建表"。

12.2 FITS 内存映射读取超大文件

# 不全量载入内存,数据在访问时才从磁盘读取
t = Table.read('huge_catalog.fits', memmap=True)

subset = t[10000:10100]  # 只触发所需分片的 IO,效率高

memmap=True 对 GB 级巡天星表尤其关键,可避免一次性 OOM;配合 skill 依赖中的 dask(见 tests/skill-requirements.toml),可进一步做大表的分块/并行处理。

12.3 区分视图与拷贝

# 视图:共享底层数据,操作快(修改会影响原表)
t_view = t['ra', 'dec']

# 拷贝:数据相互独立
t_copy = t['ra', 'dec'].copy()

需要强调:列切片默认返回视图(共享数据),若后续要独立修改,务必显式 .copy()


十三、展示与格式控制

# 控制台打印
print(t)

# 浏览器可视化(jsviewer=True 支持交互式排序/筛选)
t.show_in_browser()
t.show_in_browser(jsviewer=True)

# 分页浏览(交互式逐屏查看,类比 Unix more)
t.more()

# 自定义列格式(旧式 % 与新版 str.format 均支持)
t['flux'].format = '%.3f'
t['ra'].format = '{:.6f}'

十四、格式互转:连接 NumPy / pandas / dict

# 转 NumPy 数组(结构化数组)
arr = np.array(t)

# 转 pandas DataFrame
df = t.to_pandas()

# 转字典(列名 → 列对象)
d = {name: t[name] for name in t.colnames}

与 2.4 节的 Table.from_pandas 组合,即可实现 NumPy ↔ Astropy Table ↔ pandas DataFrame 三者任意互转,满足不同下游库的输入需求。


十五、综合实战:两个经典天文用例

15.1 双星表交叉匹配(cross-match)

以赤经赤纬为单位建 SkyCoord,再按角距离阈值筛选匹配对,是巡天数据合并的标准流程:

from astropy.coordinates import SkyCoord, match_coordinates_sky

# 从表列构造坐标对象
coords1 = SkyCoord(t1['ra'], t1['dec'], unit='deg')
coords2 = SkyCoord(t2['ra'], t2['dec'], unit='deg')

# 为 coords1 的每个源找 coords2 中最近邻
idx, sep, _ = coords1.match_to_catalog_sky(coords2)

# 以 1 角秒为阈值筛掉非真匹配
max_sep = 1 * u.arcsec
matches = sep < max_sep

# 生成互相配对的子表
t1_matched = t1[matches]
t2_matched = t2[idx[matches]]

sep 为带 arcsec 单位的角距离 Quantity,可直接与 max_sep 比较。该用例也在 SKILL.md 的 Common Workflows 中作为标准星表交叉匹配示例出现,完整的坐标系转换细节见 references/coordinates.md

15.2 按星等分箱(binning)

利用 np.digitize 把连续量离散到分箱索引,再交给 group_by 聚合,即可快速得到星等亮度函数:

# 按星等分箱:0.5 mag 步长,覆盖 10~20 mag
mag_bins = np.arange(10, 20, 0.5)
binned = t.group_by(np.digitize(t['mag'], mag_bins))
counts = binned.groups.aggregate(len)

binned.groups 每个子组对应一个星等 bin,aggregate(len) 即每个 bin 内的源计数——无需手写循环即可完成亮度分布统计。


十六、小结与延伸

astropy.table 提供了一条覆盖创建 → 访问 → 修改 → 排序/筛选 → 文件 I/O → 组合运算(stack/join/group)→ 单位/掩码/索引 → 性能调优的完整表格数据处理链路,且天然嵌入 Astropy 生态(unitscoordinatesio.fits)。要在天文 Agent 工作流中熟练运用本主题,建议:

  1. 凡涉及单位一律用 QTable,让量纲一致性由库来保证;
  2. 大批量读写优先 memmap、批量构造、索引检索,避免逐行与全量载入;
  3. mask 数据用 MaskedColumn + np.ma.* 统计,确保缺失值不被误当数值;
  4. 交叉匹配、坐标换算等进阶场景分别查阅 references/coordinates.mdreferences/fits.mdreferences/units.md,整体 skill 的安装版本(如 uv pip install "astropy==7.2.0",Python 3.11+)与环境说明见 SKILL.md
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
898
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
921
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
391