第六讲 数据格式 DONE
Table of Contents
1 课前准备
安装 HDF5 和 CSV 的相关工具。
apt install vitables python3-h5py hdf5-tools csvtool
2 数据格式
到目前为止的输入和输出还比较简单和原始,形式上都是一个一个数字操作。如果这种数据达到几百TB,操作就太困难了。这就需要更高级的数据格式,作为中间结果的数据,最重要的是遵循透明原则。它默认由计算机处理,透明原则要求它易于被理解和检查。我们重点学习满足透明原则的三个范例 CSV、HDF5 和 JSON。 程序在运行时,数据在计算机内存中临时存储。要把数据输入和输出,是把内存和外部存储之间通信,本质上是数据格式转化的过程。浮点数在转化时,会有损失,在考虑数据格式的时候,尤其要注意。在精度可行的前提下,考虑转化是否方便。数据转化是工具的功能,它们承载了数据中的信息,设计目标是让人方便地写入和读出。在实验进行中,我们会很乐意看看,数据文件中都记录了什么,实验仪器的状态是什么样子的。在处理过程中,我们也有判断中间结果是否合理的需求。 因此数据格式方便与否,直接关系到研究中的日常体验。
2.1 CSV
CSV 是 comma separated values,本身就是文本文件,可以直接用编辑器打开。CSV 可以由 Excel 等 spreadsheet 工具打开,与图形界面工具兼容性良好。CSV 符合我们的书写习惯,每一行从左往右写,写完一行写下一行,在文本文件中把数据排成二维的表格。这种方法对整数和字符串尤其有效。现在浮点数要格外注意,用文本描述小数时,难免有精度损失,应当谨慎评估。 二维表格是 CSV 的特点,也是种限制:它无法直接表达高维的数据。但是这个限制不本质,在本书第四章“关系代数”部分,我们会看到一切数据都可以归结为表格。 NumPy 中自带了处理 CSV 文件的功能。这里 “comma separated values” 已经未必是“comma”(逗号)分隔了,而是广义的“分隔符”,可以是空格或制表符(TAB)甚至是句号——只要不引入歧义都可以。 我们生成一个 (10, 10) 形状的二维数组试一试
import numpy as np s100 = np.arange(100).reshape((10, 10)) print(s100)
[[ 0 1 2 3 4 5 6 7 8 9] [10 11 12 13 14 15 16 17 18 19] [20 21 22 23 24 25 26 27 28 29] [30 31 32 33 34 35 36 37 38 39] [40 41 42 43 44 45 46 47 48 49] [50 51 52 53 54 55 56 57 58 59] [60 61 62 63 64 65 66 67 68 69] [70 71 72 73 74 75 76 77 78 79] [80 81 82 83 84 85 86 87 88 89] [90 91 92 93 94 95 96 97 98 99]]
把它存成 CSV,
np.savetxt("s100.txt", s100, fmt="%d")
看一下它的内容
cat s100.txt
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
savetxt
默认的格式是 %.18e
,即18位的科学计数法。对整数来讲,使用 %d
可增加可读性。这个数字转字符串语法约定承袭于 C,叫做“printf format string”。
输出的 CSV 文件由空格分割。每行 10 个数字,一共 10 行。直觉上看,这就是个 \(10 \times \) 的表格。一个可能的麻烦是,带空格的字符串出现在 CSV 输出里,怎么办?它的解决方案没有一定之规,有加引号的,有把分隔符换成其它字符的。遗憾的是并没有形成完整的约定,各方案未必兼容,从而解读出错误的数据。
2.1.1 读入
np.loadtxt("s100.txt", dtype=int)
array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [10, 11, 12, 13, 14, 15, 16, 17, 18, 19], [20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [30, 31, 32, 33, 34, 35, 36, 37, 38, 39], [40, 41, 42, 43, 44, 45, 46, 47, 48, 49], [50, 51, 52, 53, 54, 55, 56, 57, 58, 59], [60, 61, 62, 63, 64, 65, 66, 67, 68, 69], [70, 71, 72, 73, 74, 75, 76, 77, 78, 79], [80, 81, 82, 83, 84, 85, 86, 87, 88, 89], [90, 91, 92, 93, 94, 95, 96, 97, 98, 99]])
dtype=int
保证以整数解读文件,否则数字读入时会被暗中转化成浮点型。
NumPy 还给出了更多种类型,用于平衡精度与空间占用,包括整形的 int16
int32
int64
,浮点型的 =float16
float32
float64
float128
等。数字代表了所占的比特位数。
np.loadtxt("s100.txt", dtype=np.int16)
array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [10, 11, 12, 13, 14, 15, 16, 17, 18, 19], [20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [30, 31, 32, 33, 34, 35, 36, 37, 38, 39], [40, 41, 42, 43, 44, 45, 46, 47, 48, 49], [50, 51, 52, 53, 54, 55, 56, 57, 58, 59], [60, 61, 62, 63, 64, 65, 66, 67, 68, 69], [70, 71, 72, 73, 74, 75, 76, 77, 78, 79], [80, 81, 82, 83, 84, 85, 86, 87, 88, 89], [90, 91, 92, 93, 94, 95, 96, 97, 98, 99]], dtype=int16)
NumPy 数组与列表的区别,在于它一定有数据类型,即使在没有类型时,也要有统一的 object
类型。
有如此多的数据类型,如何知道每个 CSV 文件适合用哪种?如果用错了,轻则会损失精度,重则会产生量级的差别。这样每次读数据之后,都要检查一遍会不会读错,检查分隔符是否用对了,“#”是否代表注释,等等。读入的正确与否没有标准,只有“与人的直觉一致”,或者在团队中有明确的约定。这是 CSV 直接用文本文体存储所带来的劣势。这一劣势伴随的优势是透明,只要能处理文本文件就可以处理 CSV。
2.2 HDF5
HDF5 意思是 Hierarchical Data Format 第 5 代。HDF 最初的设计目标是提供科学数据的“图形格式”标准,方便对数据研究绘图,揭示规律。科学数据的特点是规则、体量大,要求 HDF 数据格式具有高性能,并通过透明压缩减小资源占用。 HDF 由非盈利组织开发维护。从第4代开始在各学科尤其是天体物理领域流行起来。由 NASA 选定,很多海量望远镜数据都通过 HDF 格式存储。1998 年 HDF 到了第 5 代,很多物理实验和超算中心开始采用 HDF5 。从 1.8 版本的 HDF5 开始,netCDF4 (另一个在天文观测中广泛使用的格式) 与 HDF5 正式统一。 相比于 CSV, HDF5 的好处是带有数据类型,这样做的代价是不能按照文本文件读写了,需要专门的查看器来贯彻“透明”原则。通过制定开放的工业标准,让 HDF5 的格式良好定义,允许几乎所有程序语言的第三方程序对它进行读写,可以增强它的“透明”性。这使得从早期开始,HDF 基础之上就有大量数据分析工具涌现。 HDF5 具有数据的原始(raw)表示,即 HDF 中保存的是与内存同样标准的整数、浮点数,不会有类似 CSV 的精度损失。HDF5 的数据类型自我描述,在读入内存时不需要额外的信息源,因为 HDF5 文件中包含了数据类型和长度等辅助信息。 HDF5 的一个潜在缺点是无法处理中文,在它的标准在制定时只考虑了英文字符。为了保证它的兼容性,尽量不使用英文字母以外的字符。
2.2.1 HDF5 的结构
HDF5 文件结构分三种。
数据集 Dataset 与 NumPy 多维数组很像,数据类型多种多样可自定义。组织整理数据集要类,
可以用组 Group 。组的嵌套关系用“/”表达,语法与文件夹一致,例如 /calibration/water/waveform
calibration 和 water 是组, waveform 是数据集。元数据 metadata 作为数据集或者组的标签,例如通过 metadata 标记 /calibration/water
组的温度为 25。记录实验条件信息,可以使用实验记录本。但是一次原则的指导下,最好相关的信息写在同一处, 元数据的设计正是为了提供此便利。
2.2.2 HDF5 的 Python 工具
Python 上流行的 HDF5 工具有两种,较底层极简的 h5py 和有高级功能自定义格式的 PyTables。由于 h5py 的 HDF5 原始格式与其它语言的兼容性更强,更符合标准,我们本着透明原则选用 h5py。其它工具能正常读写数据,远比 20% 的性能提升重要。 h5py 缩写的含义是 HDF5 Python。它的本质是 Python 调用 HDF5 C 语言库的接口,因此与使用 HDF5 标准格式无差别。
装载 h5py ,看一下它的 test。
import h5py with h5py.File("s100.h5", "w") as opt: opt["s100"] = s100
在当前位置写入一个 s100.h5
的文件。从文件系统可访问到它。
file s100.h5 h5dump -A s100.h5
s100.h5: Hierarchical Data Format (version 5) data HDF5 "s100.h5" { GROUP "/" { DATASET "s100" { DATATYPE H5T_STD_I64LE DATASPACE SIMPLE { ( 10, 10 ) / ( 10, 10 ) } } } }
file
识别出了它是 “Hierarchical Data Format (version 5) data”, HDF5 的工具 h5dump
给出它的内部信息,一个 H5T_STD_I64LE
数据类型,即 64 位整型的, (10, 10) 数组,与 NumPy 的原始类型一致。
不用 -A
参数时, h5dump
能查看全部的数据
h5dump s100.h5
HDF5 "s100.h5" { GROUP "/" { DATASET "s100" { DATATYPE H5T_STD_I64LE DATASPACE SIMPLE { ( 10, 10 ) / ( 10, 10 ) } DATA { (0,0): 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, (1,0): 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, (2,0): 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, (3,0): 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, (4,0): 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, (5,0): 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, (6,0): 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, (7,0): 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, (8,0): 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, (9,0): 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 } } } }
H5T_STD_I64LE
存储 100 以内的数据太浪费,只要8位就够了。我们把 NumPy 的数组转成 8 位整型后,保存到 HDF5。
with h5py.File("s100-int8.h5", "w") as opt: opt["s100"] = s100.astype(np.int8)
h5dump s100-int8.h5
HDF5 "s100-int8.h5" { GROUP "/" { DATASET "s100" { DATATYPE H5T_STD_I8LE DATASPACE SIMPLE { ( 10, 10 ) / ( 10, 10 ) } DATA { (0,0): 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, (1,0): 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, (2,0): 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, (3,0): 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, (4,0): 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, (5,0): 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, (6,0): 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, (7,0): 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, (8,0): 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, (9,0): 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 } } } }
确认数据类型变成了 H5T_STD_I8LE
,但是内容不变。文件大小
ls -lh s100*.h5
-rw-r--r-- 1 xubd xubd 2.8K Jul 19 11:51 s100.h5 -rw-r--r-- 1 xubd xubd 2.1K Jul 19 12:07 s100-int8.h5
int8
存储的确实节省了空间,但是注意它能表示的范围只有 -128 至 127。
注意,在 h5py.File
是大写的 File
,在写入数据集时,例子中使用了 opt["s100"] = s100
,当作字典来使用。写入的风格与 CSV 有所差异,它们相同的地方仅仅是都打开一个文件,但具体如何打开,打开后如何操作,不同的作者有不同的约定。多种形式难以记住,可随时查阅在线帮助。
2.2.3 读取 HDF5
with h5py.File("s100.h5", 'r') as ipt: h5_s100 = ipt["s100"][...] print(h5_s100) print(h5_s100.dtype)
[[ 0 1 2 3 4 5 6 7 8 9] [10 11 12 13 14 15 16 17 18 19] [20 21 22 23 24 25 26 27 28 29] [30 31 32 33 34 35 36 37 38 39] [40 41 42 43 44 45 46 47 48 49] [50 51 52 53 54 55 56 57 58 59] [60 61 62 63 64 65 66 67 68 69] [70 71 72 73 74 75 76 77 78 79] [80 81 82 83 84 85 86 87 88 89] [90 91 92 93 94 95 96 97 98 99]] int64
我们也用了 with h5py.File
,默认是读模式。和写时一致,文件读的操作器 handler 也能当成字典使用。在调用 ipt["s100"]
时,后面要加 [...]
,代表把所有数据读进内存。
操作器的类型是 h5py._hl.files.File
type(ipt)
h5py._hl.files.File
它并不是字典,但是模拟了字典的接口。这是工具接口的常见设计思想,模仿一个大家都熟悉的工具的接口。
with h5py.File("s100-int8.h5") as ipt: print(type(ipt.keys())) print(list(ipt.keys())) print(ipt["s100"])
<class 'h5py._hl.base.KeysViewHDF5'> ['s100'] <HDF5 dataset "s100": shape (10, 10), type "|i1">
i表示整数,1表示一个字节,即 int8。把 s100
取出时,HDF5 自我描述可自动把 NumPy 的类型设置好。
with h5py.File("s100-int8.h5") as ipt: h5_s100 = ipt["s100"][...] print(h5_s100) print(h5_s100.dtype)
[[ 0 1 2 3 4 5 6 7 8 9] [10 11 12 13 14 15 16 17 18 19] [20 21 22 23 24 25 26 27 28 29] [30 31 32 33 34 35 36 37 38 39] [40 41 42 43 44 45 46 47 48 49] [50 51 52 53 54 55 56 57 58 59] [60 61 62 63 64 65 66 67 68 69] [70 71 72 73 74 75 76 77 78 79] [80 81 82 83 84 85 86 87 88 89] [90 91 92 93 94 95 96 97 98 99]] int8
读取数据时后面的 [...]
或者 [()]
,用来把整个数据载入内存。但有时数据非常大,内存装不下,HDF5有方法把文件分块读入,逐块读入内存处理。此操作,叫做 “out of core computing”,又称 “external memory algorithm”。
类似于 HDF5 文件给出类字典的接口,它的数据集 dataset 提供的是类 NumPy 数组接口。后者是 Python 科学计算领域既有标准。下面展示 HDF5 数据集里,模拟 NumPy 数组的典型特征,包括数据类型和索引等。
with h5py.File("s100-int8.h5") as ipt: print(ipt['s100'].dtype) print(ipt['s100'][::2, ::3])
int8 [[ 0 3 6 9] [20 23 26 29] [40 43 46 49] [60 63 66 69] [80 83 86 89]]
不同的是 HDF5 dataset 支持 out of core computing。
2.2.4 HDF5 的组
HDF5 的组可与文件系统中的文件夹类比。创建组使用 creat_group
函数。
with h5py.File("hzg.h5", "w") as opt: opt.create_group("/home") opt["home"]["s100"] = s100
在命令行确认。
h5dump -A hzg.h5
HDF5 "hzg.h5" { GROUP "/" { GROUP "home" { DATASET "s100" { DATATYPE H5T_STD_I64LE DATASPACE SIMPLE { ( 10, 10 ) / ( 10, 10 ) } } } } }
s100
的数组集被放在了 home
的组之下,注意 HDF5 文件都有一个 /
的默认的组。读取试试。
with h5py.File("hzg.h5", "r") as ipt: print(type(ipt["home"])) print(type(ipt["home"]["s100"])) print(type(ipt["home/s100"]))
<class 'h5py._hl.group.Group'> <class 'h5py._hl.dataset.Dataset'> <class 'h5py._hl.dataset.Dataset'>
2.2.5 移动数组集
HDF5 移动操作,可以用复制和删除组合实现。我们把 /home/s100
移动到 /s100
。
with h5py.File("hzg.h5", "a") as ipt: ipt["s100"] = ipt["home/s100"] del ipt["home/s100"]
这里打开文件的选项是“a”,意思为 append ,既读又写。
h5dump -A hzg.h5
HDF5 "hzg.h5" { GROUP "/" { GROUP "home" { } DATASET "s100" { DATATYPE H5T_STD_I64LE DATASPACE SIMPLE { ( 10, 10 ) / ( 10, 10 ) } } } }
文件修改后, s100
数据集与 home
组并列,都在同一个层次了。
HDF5 的组与数据集,与文件系统神似,有非常强的表现力,可以表征大多数的数据存储情形。它在大规模的数据处理中非常方便。例如 MATLAB 的 mat
文件,就是建立在 HDF5 标准之上。可见 HDF5 对工业界和学术界的影响深远。HDF5 的兼容性使得 Python 可以与其它语言,如 R、C++、MATLAB 进行数据交换,增加分工合作。
2.3 JSON
当数据没有整齐形态,可能伴随有分支、嵌套等时,使用JSON更方便。JSON 与 Javascript 有 很深的渊源,在网页前端开发中应用广泛。Javascript 是应该最广的程序语言,我们在浏览网页时,都会在浏览器运行 Javascript 程序。
JSON 是 JavaScript Object Notation 的缩写,读作 “Jason”。。JSON 的作者曾经给名叫 Jason 的人道过歉,给他们的生活造成了不便。他自己也没想到 JSON 可以变得如此流行。JSON 由网站的数据结构需求来。在网页中,要更新显示动态内容,数据的载体由 JSON 提供,替代了过去的冗长不易由人类阅读的 XML 格式。JSON 提供了与 XML 等价的逻辑结构,但更易阅读因此被迅速采用,成为了国际标准:透明原则。 JSON 借鉴了 Python 字典和列表的语法,与 Python 交互极其方便。但是 JSON 面向的纯文本数据,与 CSV 类似,对数字的表现力弱。在科学实验里,JSON 可用来表征有复杂层次关系的,不齐整的实验条件数据。这可与 HDF5 的元数据有效互补。 下载一个 JSON 数据样例,取自 LIGO 的公开数据集。
wget 'http://hep.tsinghua.edu.cn/~orv/pd/BBH_events_v3.json'
--2022-07-24 16:12:38-- http://hep.tsinghua.edu.cn/~orv/pd/BBH_events_v3.json Resolving hep.tsinghua.edu.cn... 101.6.6.219, 2402:f000:1:416:101:6:6:219 Connecting to hep.tsinghua.edu.cn|101.6.6.219|:80... connected. HTTP request sent, awaiting response... 200 OK Length: 2202 (2.2K) [application/json] Saving to: ‘BBH_events_v3.json’ BBH_events_v3.json 0%[ ] 0 --.-KB/s BBH_events_v3.json 100%[===================>] 2.15K --.-KB/s in 0s 2022-07-24 16:12:38 (215 MB/s) - ‘BBH_events_v3.json’ saved [2202/2202]
查看文件的内容。
cat BBH_events_v3.json
{ "GW150914":{ "name":"GW150914", "fn_H1" : "H-H1_LOSC_4_V2-1126259446-32.hdf5", "fn_L1" : "L-L1_LOSC_4_V2-1126259446-32.hdf5", "fn_template" : "GW150914_4_template.hdf5", "fs" : 4096, "tevent" : 1126259462.44, "utcevent" : "2015-09-14T09:50:45.44", "m1" : 41.743, "m2" : 29.237, "a1" : 0.355, "a2" : -0.769, "approx" : "lalsim.SEOBNRv2", "fband" : [43.0,300.0], "f_min" : 10.0 }, "LVT151012":{ "name":"LVT151012", "fn_H1" : "H-H1_LOSC_4_V2-1128678884-32.hdf5", "fn_L1" : "L-L1_LOSC_4_V2-1128678884-32.hdf5", "fn_template" : "LVT151012_4_template.hdf5", "fs" : 4096, "tevent" : 1128678900.44, "utcevent" : "2015-10-12T09:54:43.44", "m1" : 44.111, "m2" : 11.205, "a1" : 0.447, "a2" : -0.434, "approx" : "lalsim.SEOBNRv2", "fband" : [43.0,400.0], "f_min" : 10.0 }, "GW151226":{ "name":"GW151226", "fn_H1" : "H-H1_LOSC_4_V2-1135136334-32.hdf5", "fn_L1" : "L-L1_LOSC_4_V2-1135136334-32.hdf5", "fn_template" : "GW151226_4_template.hdf5", "fs" : 4096, "tevent" : 1135136350.65, "utcevent" : "2015-12-26T03:38:53.65", "m1" : 19.6427, "m2" : 6.7054, "a1" : 0.3998, "a2" : -0.0396, "approx" : "lalsim.SEOBNRv2", "fband" : [43.0,800.0], "f_min" : 10.0 }, "GW170104":{ "name":"GW170104", "fn_H1" : "H-H1_LOSC_4_V1-1167559920-32.hdf5", "fn_L1" : "L-L1_LOSC_4_V1-1167559920-32.hdf5", "fn_template" : "GW170104_4_template.hdf5", "fs" : 4096, "tevent" : 1167559936.6, "utcevent" : "2017-01-04T10:11:58.60", "m1" : 33.64, "m2" : 24.82, "a1" : -0.236, "a2" : 0.024, "approx" : "lalsim.SEOBNRv2", "fband" : [43.0,800.0], "f_min" : 10.0 }
简直就是 Python 的字典!
2.3.1 读取 JSON
import json with open("BBH_events_v3.json", "r") as ipt: events = json.load(ipt) print(type(events)) # 就是一个字典 print(events.keys())
<class 'dict'> dict_keys(['GW150914', 'LVT151012', 'GW151226', 'GW170104'])
注意要先把文件按照文本形式打开,用 open
,再转成 JSON 的格式 json.load
。 events 完全就是 Python 字典,其中的词恰好对应文件中的 4 个部分。
把第一组值读出。
events["GW150914"]
{'name': 'GW150914', 'fn_H1': 'H-H1_LOSC_4_V2-1126259446-32.hdf5', 'fn_L1': 'L-L1_LOSC_4_V2-1126259446-32.hdf5', 'fn_template': 'GW150914_4_template.hdf5', 'fs': 4096, 'tevent': 1126259462.44, 'utcevent': '2015-09-14T09:50:45.44', 'm1': 41.743, 'm2': 29.237, 'a1': 0.355, 'a2': -0.769, 'approx': 'lalsim.SEOBNRv2', 'fband': [43.0, 300.0], 'f_min': 10.0}
是一个嵌套的字典。
2.3.2 写 JSON
输出 JSON 时,使用 dump
函数。NumPy 用 loadtxt
savetxt
,HDF5 用 File
,JSON 是 load
和 dump
。不同的命名风格来自不同的开发团队,注意其中的区别。
with open("BBH_events_rewrite.json", 'w') as opt: json.dump(events, opt)
新输出的文件对人不友好。
cat BBH_events_rewrite.json
{"GW150914": {"name": "GW150914", "fn_H1": "H-H1_LOSC_4_V2-1126259446-32.hdf5", "fn_L1": "L-L1_LOSC_4_V2-1126259446-32.hdf5", "fn_template": "GW150914_4_template.hdf5", "fs": 4096, "tevent": 1126259462.44, "utcevent": "2015-09-14T09:50:45.44", "m1": 41.743, "m2": 29.237, "a1": 0.355, "a2": -0.769, "approx": "lalsim.SEOBNRv2", "fband": [43.0, 300.0], "f_min": 10.0}, "LVT151012": {"name": "LVT151012", "fn_H1": "H-H1_LOSC_4_V2-1128678884-32.hdf5", "fn_L1": "L-L1_LOSC_4_V2-1128678884-32.hdf5", "fn_template": "LVT151012_4_template.hdf5", "fs": 4096, "tevent": 1128678900.44, "utcevent": "2015-10-12T09:54:43.44", "m1": 44.111, "m2": 11.205, "a1": 0.447, "a2": -0.434, "approx": "lalsim.SEOBNRv2", "fband": [43.0, 400.0], "f_min": 10.0}, "GW151226": {"name": "GW151226", "fn_H1": "H-H1_LOSC_4_V2-1135136334-32.hdf5", "fn_L1": "L-L1_LOSC_4_V2-1135136334-32.hdf5", "fn_template": "GW151226_4_template.hdf5", "fs": 4096, "tevent": 1135136350.65, "utcevent": "2015-12-26T03:38:53.65", "m1": 19.6427, "m2": 6.7054, "a1": 0.3998, "a2": -0.0396, "approx": "lalsim.SEOBNRv2", "fband": [43.0, 800.0], "f_min": 10.0}, "GW170104": {"name": "GW170104", "fn_H1": "H-H1_LOSC_4_V1-1167559920-32.hdf5", "fn_L1": "L-L1_LOSC_4_V1-1167559920-32.hdf5", "fn_template": "GW170104_4_template.hdf5", "fs": 4096, "tevent": 1167559936.6, "utcevent": "2017-01-04T10:11:58.60", "m1": 33.64, "m2": 24.82, "a1": -0.236, "a2": 0.024, "approx": "lalsim.SEOBNRv2", "fband": [43.0, 800.0], "f_min": 10.0}}
使用 jq
命令可以更好地阅读。
jq < BBH_events_rewrite.json
{ "GW150914": { "name": "GW150914", "fn_H1": "H-H1_LOSC_4_V2-1126259446-32.hdf5", "fn_L1": "L-L1_LOSC_4_V2-1126259446-32.hdf5", "fn_template": "GW150914_4_template.hdf5", "fs": 4096, "tevent": 1126259462.44, "utcevent": "2015-09-14T09:50:45.44", "m1": 41.743, "m2": 29.237, "a1": 0.355, "a2": -0.769, "approx": "lalsim.SEOBNRv2", "fband": [ 43.0, 300.0 ], "f_min": 10.0 }, "LVT151012": { "name": "LVT151012", "fn_H1": "H-H1_LOSC_4_V2-1128678884-32.hdf5", "fn_L1": "L-L1_LOSC_4_V2-1128678884-32.hdf5", "fn_template": "LVT151012_4_template.hdf5", "fs": 4096, "tevent": 1128678900.44, "utcevent": "2015-10-12T09:54:43.44", "m1": 44.111, "m2": 11.205, "a1": 0.447, "a2": -0.434, "approx": "lalsim.SEOBNRv2", "fband": [ 43.0, 400.0 ], "f_min": 10.0 }, "GW151226": { "name": "GW151226", "fn_H1": "H-H1_LOSC_4_V2-1135136334-32.hdf5", "fn_L1": "L-L1_LOSC_4_V2-1135136334-32.hdf5", "fn_template": "GW151226_4_template.hdf5", "fs": 4096, "tevent": 1135136350.65, "utcevent": "2015-12-26T03:38:53.65", "m1": 19.6427, "m2": 6.7054, "a1": 0.3998, "a2": -0.0396, "approx": "lalsim.SEOBNRv2", "fband": [ 43.0, 800.0 ], "f_min": 10.0 }, "GW170104": { "name": "GW170104", "fn_H1": "H-H1_LOSC_4_V1-1167559920-32.hdf5", "fn_L1": "L-L1_LOSC_4_V1-1167559920-32.hdf5",
或者给 dump
函数加入 indent=2
的参数。
3 软件管理器
我们的 GNU 系统环境中,都带有软件管理器,例如 apt
。需要什么工具可以随手安装,只要网络足够快,就能快速安装和使用,非常方便。这个工具叫包管理器 package manager 。