感谢@shakuna 录制教学视频以及提供视频文本。转载已经过同意。
欧洲原子能中心大数据处理软件Sern ROOT教程三:示例演示与入门_哔哩哔哩 (゜-゜)つロ 干杯~-bilibiliwww.bilibili.com![abb8fbcf959a1d1ed1512e6dac5cdb2a.png](https://i-blog.csdnimg.cn/blog_migrate/861a438f6dfcb08350444b63f08b96e3.jpeg)
大家好,这里是更新的ROOT学习视频。这一节主要还是演示ROOT的基本功能,同时讲几个面对新手的专题。考虑到很多学物理的同学对程序之类的不熟悉,这节也有一些心得和推荐书目。不过,用多了也就熟悉了。在最后有时间的话,来讨论几个初学者经常有困惑的专题。本视频全程无声,请放心播放你喜欢的音乐作为bgm。
ROOT是Sern开发的统计分析及作图软件,用来处理高能物理实验产生的海量(GB-TB以上)数据,同时作出精美的图片。它主要以C++语言写就,也通过增加接口、库的方式在融合python(pyROOT),ruby,R,jupyter语言。
笔者主要以ubuntu linux系统来做视频。在科研服务器上,常用的操作系统是Cent OS version 7, 它和苹果电脑mac自带的macOS系统(基于Unix)以及ubuntu大同小异。而对于个人电脑,ubuntu更加常用(有广泛的兼容性、漂亮的界面、活跃的社区等等,虽然问题也很多!);而mac比较贵。至于windows,虽然ROOT 6 pro(2017年~)已经支持,但不是很推荐,因为功能不是很齐全,有时候会有一些很奇怪的问题。总之,使用Unix/Linux更加顺畅。
常用的Linux编辑器:vim, emacs(编辑器之神和神之编辑器), geany(轻量的代码编辑器),sublime text。 笔者习惯用vim来写代码。现在这个是typora,主要用来方便地显示文本。
演示系统:ubuntu 18.04
ROOT 5.34.36/6.18.00,编译器gcc4.8.5(C++98 )/gcc7(C++11)
视频演示
- Linux下纯C++文件
运行Linux自带的g++编译器
这是一个单纯的C++程序,以.C, .cpp, .cxx结尾g++ test.cpp -o prog1
使用Linux自带的C++编译器:gnu g++来编译这段程序,生成可执行的对象(-o) prog1
之后,我们看到文件里多了一个prog1,这个实际上就是编译后生成的可执行文件(类似windows的exe文件)。在Linux命令行里,使用./来运行这个文件。你也可以去文件夹里,鼠标双击prog1运行。然后我们在命令行里看到执行打印的结果。
另一个复杂的编译方法是使用make(scons),一般用于复杂程序项目的编译。以后有机会会演示。
ROOT自带了一个方便的C++命令行解释器CINT,如你们所知,在里面可以无视C++的严格规范(比如打印时不需要include,这是因为CINT已经默认登录了一些必要的库)来直接运行一些命令。 - 运行ROOT的CINT界面
- 运行ROOT macro
- 编译并运行ROOT的C++程序
运行ROOT的时候需要先source。
source是什么意思呢?在windows里,执行一个软件,一般在桌面双击这个软件的图标。文件夹里的软件图标,其实就是一个快捷的链接,通过双击的动作访问并执行安装在某处(比如C盘的软件内容)。在Linux里使用命令行的时候,执行source命令和双击相似,主要告诉系统要访问安装在某处的某个软件的内容。
每次打开终端,不用老是切换到文件夹里然后source thisroot.sh
可以在.bashrc文件(注意“.”后面没有空格)里进行设置(插入):
. ~/ubuntu_software/root6/bin/thisroot.sh
这里,“~/ubuntu_software/root6/”这一大堆可以用$ROOTSYS来代替。
实际上,在thisroot.sh这个文件中,有相关代码把用户较长的安装路径统一简化为了$ROOTSYS
视频演示
在使用Linux的过程中,你会知道:你对计算机执行的每一个动作,背后都有一大堆源代码支持。你可以很方便地查看并理解背后的机制。而Windows因为是商业系统,把这些都封装了起来(黑盒子),不让用户接触。这就是平常所说的,学习Linux能更了解计算机的原因。
句点.是source命令的缩写,凡是命令后面要加空格,然后再执行内容。
或者
alias root6='. ~/ubuntu_software/root6/bin/thisroot.sh'
这里的意思是,自定义(别名)了一个“root6”命令,这个命令的内容是source root6文件夹里的thisroot.sh。这样,打开终端后输入root6 就是source命令。
这样是为了便于使用多个版本的ROOT。有些老程序使用ROOT version 5(笔者主要用这个!)。版本的主要区别在于C++内核,ROOT-5是C++ 98, ROOT-6是新的C++11,里面优化了C++的一些语句,也添加了新的功能(比如auto类型)。关于C++,推荐的教材是On to C++(用来入门的很短小的册子)以及C++ Primer(很细致,可以当字典查)。
.bashrc文件是一个shell脚本配置文件,在每次打开终端时都会自动运行。所以,你可以在这里写脚本语句(相当于编程)。
如果你想成为Linux大佬掌握Shell脚本语言(很多有用的Linux技巧,比如配置文件等等),非常推荐:
![3cf9e7254855f295c43dfc2b53709190.png](https://i-blog.csdnimg.cn/blog_migrate/8afaab4bb827e7d4f1f8d9ba466b6110.jpeg)
自己去读就行了。很多Linux命令脚本都可以通过python来实现(有时候比较蛋疼,有点兜圈子的意味),比如批量运行ROOT,批量上传下载,或者批量向科研服务器递交任务等等(类似windows的批处理bat)。但是要提一下,awk和sed这两种脚本语言是专门来做这些事情的,所以原理上比python效率要高点(也更加高大上,可以算作是是否熟练掌握了shell命令的一个标志)。
如何学习ROOT? 一本Diving into ROOT足矣。一边阅读一边运行上面的例子,搞完这个60多页的小册子,你就是ROOT初级高手了。之后呢?根据工作需要,去查看 $ROOTSYS/tutorials 里面的例子。
视频演示
最典型的:
- 读写文件 tutorials/tree/
- 数据拟合 tutorials/tree/fit
运行几个实例试试!
然后一边做科研任务,有不会的去问、去学,然后成为中级高手。如何达到高阶?在ROOT论坛和github上给ROOT添砖加瓦吧。但前者人人都能注册,后者需要一个Sern的内部帐号。
ROOT是Sern的开源软件,理论上Sern的科学家会不断把成熟的或者最新的算法成果整合到ROOT的库中,比如快速傅立叶变换使用了MIT的fftw算法库,ROOT的TMINUIT求最小化软件包改写自Fortran的MINUIT包(实际也来自Sern科学家在70年代的工作),对微弱信号进行统计分析(构造置信带)的Feldman-Cousin方法(arxiv9711021)也已有了TFeldmanCousins。不必自己去造轮子!
ROOT的精髓在于Tree,因此它自带的类、库都用T开头。
有一点要记住,学ROOT只是学一个工具,在其背后,你需要理解的是数据处理、统计分析等知识。 举个例子(深入了解后可以回来想想),某个物理量应该满足均匀分布,但是在分析的时候却试图进行高斯拟合。尽管使用了复杂的算法使得拟合较为成功,但原理上却是错误的。切记,很多时候,你的问题可能在原理上,而不是编程上。在ROOT论坛上,曾有人询问ROOT生成的拟合的输出结果代表什么意思,被ROOT的开发者之一Rene怒怼:请查看维基百科或者阅读统计分析的教材。
如果你对这些方面的原理抱有困惑,那么下面四本参考书是很有价值的:
![c5054a24af0bf71d403f56e28be4f413.png](https://i-blog.csdnimg.cn/blog_migrate/fa7baf96f53ed2d368825c453f6d4778.png)
![58dfce9ce90e0d70e2c2ea964bd4e7dc.png](https://i-blog.csdnimg.cn/blog_migrate/1811c5808bddee59ab2fb2f597a3f487.png)
朱永生是丁肇中的学生。
![bd4ee4df77e023529ffe5d8d76dcba8e.png](https://i-blog.csdnimg.cn/blog_migrate/6ceda32347f4e0ff8f9fd46b6f8412da.png)
![8a043b87414e36e170f7fd04c5da8f82.png](https://i-blog.csdnimg.cn/blog_migrate/26488914a65fae0cfebf761f42fbc94a.png)
视频演示 Diving into ROOT的一些例子,geom(比较有意思),pyTMVA
视频演示 基础中的基础:了解和编译C++
注意纯C++和ROOT宏的区别。宏是一组命令的集合,用{}包括起来。可以把include等等忽略,它利用了CINT。运行的时候,使用root这个命令,后面跟宏文件名。运行完后,直接进入CINT显示结果。
如果后面跟上加号“+”,表示使用了C++的链接编译。我们在运行复杂的程序项目时,用到了其他文件,则需要完整地写C++程序而不只是宏,因为此时需要链接其他的文件和库,宏对此无能为力。宏只是一组命令的集合
ROOT的解释器,宏(macro)
视频演示
注:ROOT显示几何图形的glew窗口只有ROOT6有。
ubuntu需要sudo apt-get install libglew-dev
这里演示了一些著名实验装置的几何结构。D0是发现顶夸克的实验装置。
MP3play这个演示,实际可以实现显示时间、播放歌曲等mp3功能!但是这里太卡不弄了。
tank其实可以联想到核物理在军事上的研究,比如中子弹产生的中子如何穿透钢板等等。实际上,目前也是可以通过模拟研究的:画出几何体,然后模拟粒子在几何体里的穿行。但是,ROOT一般不做这个,需要另一个由斯坦福直线加速中心开发的Geant4软件(也是基于C++)。
TMVA是2013年左右,合成到ROOT里的一个机器学习库。
这里运行pyROOT的TMVA决策树。
这个决策树,用来对两种不同的数据进行机器学习后的分类。
这属于科研里常用的ROOT高级功能。
演示pyROOT,可以在jupyter notebook上运行,体验有点像Matlab. Ctrl+enter。这里可以运行python版的ROOT。注意和C++语法的切换!
初学 Topic1: 直方图与ntuple
直方图可能是初学者首先碰到的一个困惑。
我们来谈两种理解:
(1)分段函数进行计数
视频演示
以加拿大人口统计为例https://www12.statcan.gc.ca/census-recensement/2016/dp-pd/dt-td/Rp-eng.cfm?LANG=E&APATH=3&DETAIL=0&DIM=0&FL=A&FREE=0&GC=0&GID=0&GK=0&GRP=1&PID=109525&PRID=0&PTYPE=109445&S=0&SHOWALL=0&SUB=0&Temporal=2016&THEME=115&VID=0&VNAMEE=&VNAMEF=
我们想根据数据,作出年龄和人口关系分布的直方图。
在这里做直方图,相当于计算分段函数。我们以1岁为单位,把人口分为0-1, 1-2, 2-3, … ,99-100, 100+。我们可以设想,有一亿个的人样本,我们对他们一个个调查,他/她的年龄如果是35, 就归到35-36这个这个桶(bin)里,在这个bin里计数加1(注意,这里有个微妙的“左边”和“右边”的闭区间选择,原理上也可以放到34-35里。但是,ROOT有设置underflow和overflow,这是ROOT直方图的一个需要注意的地方!)。这样,每个年龄的bin里就是人口的计数,然后我们就能画出年龄vs人口数量的分布直方图。
笔者建立了一个.txt(或者.dat)表格,根据上面的这个网站,第1列是年龄(以1岁为单位分割);第2列是这个年龄里的人口数量;第3列是男性的人口,第4列是女性。中间用空格分开。
我们马上就来演示一下tutorials/tree/basic.C这段读取数据并记录成直方图的程序。
万事开头难,有时候因为分析方面的工具已经开发完毕,而困难在于想办法读取数据。这里使用了C++的数据流方法(iostream和<<,>>运算符)。
in表示从文件里一行行读取,比如第一行
0 369730 189085 180650
这些数据由空格分开,因此可以把它们直接“流入”(>>)到变量里
读取数据后,创建几个直方图对象。这里,年龄直方图设置为:范围0-100, 总共有100个bin。这样bin的宽度为 ,也就是我们分段函数的跨度为1年。
TH1F *h1 = new TH1F("h1","age distribution",100,0,100);
同样道理,我们也可以改变bin宽度,也就是把分段函数的划分分得更粗或更细(这里不能更细,因为人口普查不会去调查年龄有多少个月!),比如以5年为跨度,把人口分为0-5岁,…95-100岁(注意原始数据里也做了这个的统计)。这个动作称为Rebin。此时,直方图的bin宽变宽,分布变得不那么精细。这里,我们也可以得知,bin宽有时候也和分辨率有关。在数据拟合,比如对直方图作拟合时,我们有时候需要调整直方图的拟合范围和bin宽,才能达到合理的拟合结果。
视频演示
打开保存的root文件
root basic.root
使用TBrowser 浏览
在灌入直方图的时候,我们使用了两种方法:
h1->Fill(age, total);
或者h2->SetBinContent(i+1, total);
想一想,上面两个为何平均值不一致?
年龄vs总人口。年龄vs男 女 人口
ROOT已自动统计了直方图数据的平均值,即总人口平均年龄为41岁。
男性平均年龄小于女性。
(2)对连续的模拟信号进行离散化。
比如用传统的阴极射线管示波器来看电流随时间的变化,我们在电子荧幕上看到的一般是一条连续的曲线,这是自然的模拟信号。而对于数字化采样的示波器,它有一个采样率(每秒记录多少个数据,比如1GHz,代表一秒内可以记录1亿个数据,或者说机器1纳秒采一个数据)。如果采样率较低,那么我们看到的是一个个离散的点;采样率越高,点就越密集,最后记录的数据看上去更加接近连续曲线/模拟信号。
对于离散信号,我们可以把它们以直方图的形式保存。x轴的单位宽度可以用最小的采样单位作为直方图的bin宽。
比如1GHz采样率,最小单位为1ns。某些情景context下,也会用Least significant bit (LSB)来说明。
下面这个例子是笔者记录的CAEN 1GHz数字信号处理器DT5751所记录的某个电信号随时间的变化。数字信号处理器的采样时间是252 ns,即在仪器运行的时候,记录某段252ns时间内的一组电信号值。在实际的实验系统中,我们一般会设置某种触发机制,让仪器在满足某个条件后,才开始记录,而不是从头到尾一直运行,这样会把不需要的噪音也记录了。
视频演示
TH1F *hV = new TH1F("hV","waveform", 251, 0, 251)
x轴是时间t,记录范围是0-252 ns。y轴值即在1 ns内采样的电流值。
如前所述,bin宽在这里涉及到了分辨率和精度的问题。在这张图里,看x轴的话,252 ns被分割成了[0,1],[1,2], …, [250, 251] ns的bin,每个bin都有一个y值:。对于x=1.2 ns, x=1.5 纳秒,都是同一个y值,因为我们的精度没有达到0.1秒。ROOT默认对应的x值为(1+2)./2=1.5,也即binCenter。这会影响到平均值、均方根(RMS)等值的计算,需要注意!某些小技巧:
TH1F *hV = new TH1F("hV","waveform", 251, 0.5, 251.5)
这样使得binCenter为整数
初学 Topic2: ntuple
python的字典
excel有记录条数的限制。一般上限是1048576()条数据。超过6万条数据的话,电脑运行会非常缓慢,很难进行计算、作图。
对海量数据的记录、快速访问,这正是Sern发明ROOT的重要原因之一。
回来看人口统计的例子。ntuple对象将每一行(条)的数据进行了关联。而之前的h1, h2, h3只是各自Fill各自的,之间并没有关联。
在调查人口时,比如有10人的数据,数据里记录了10条信息。我们也可以看成是对他们进行了编号#1, #2, ……,#10。比如对于#2和#3,我们记录了他/她们的多条信息,这样就会有一个数据结构。参考:
python dictionary的形式:
data2 = {‘number':2, ’age': 10, 'gender':1, 'SexOrient':0}
data3 = {‘number':3, ’age': 40, 'gender':0,'SexOrient':1}
json数据结构的形式:
data2 = {number: 2,age: 10,gender: 1, SexOrient:0}
data3 = {number: 3,age: 40,gender: 0, SexOrient:1}
//性别用布尔值表示
//性取向用0,1,2表示,0:直, 1:弯, 2:双
这里保存的ntuple,每片叶子上都是单纯的数值。点击叶子,自动作出直方图。age叶子里,保持的是单纯的0,1,2,3, …, 100这些年龄值
这样组成数据结构后,我们可以进行快速作图,或者创建新的直方图,把作图结果保存进去。比如,我们可以画出女性的年龄分布:
ntuple->Draw("age",“gender==0”)
更复杂的情况,画出蕾丝的年龄分布:
ntuple->Draw("age",“gender==0 && SexOrient==1”)
第二个引号内放的是数据的切割(cut)条件。
这些后面会在TTree里演示,这里只是提一下!
TH1F *hLesbian = new TH1F("hLesbian","Lesbian age distribution",100,0,100);
ntuple->Draw("age>>hLesbian",“gender==0 && SexOrient==1”)
在此时,可以想象,因为数据结构事先已经编好,程序能够分辨出那些数值是age,哪些是女性人口数量,这样把它们再揉和到一起做图。
注意:这里演示所用的数据和上面的例子略有不同。这里并没有每一个人的具体的一组调查数据(否则是有百万条数据记录),而是已经分类好了的,以每个年龄段归类的总体信息。因此我们也可以看成这里的数据也做过了Rebin。
查看数据,也可以用ntuple的Scan功能直接去看,比如看年龄大于60岁的男女分布:
ntuple->Scan("maleNumber:femaleNumber","age>60")
ntuple->Draw("maleNumber:femaleNumber","age>60")
画出60岁以上,男女人数的比例的散点图
![36d3a68c3b5e2f77b70519d7ef6fed35.png](https://i-blog.csdnimg.cn/blog_migrate/bf04b2ed93f77765318c29adcc041141.jpeg)
生成了一个表格,最左边是默认的行数数值。
在高能物理实验中,探测器随着时间流逝,除去死时间(dead time)外,一直在记录探测器内实验所感兴趣的粒子碰撞产生的事件(event)。对每一个事件,我们可以记录一组数值:事件发生的时间,能量,动量(方向),位置,轨迹(track)等等。每一个事件都有一个编号(一般以时间顺序编号)。用TTree和ntuple把所有记录的事件的数据关联之后,我们可以作一组数据切割,从里面再挑出感兴趣的数据。比如要从一组探测器记录的数据中挑选出太阳中微子,要求事件发生在探测器的中心,半径小于3m的地方、能量大于5MeV、方向指向太阳。把这些事件挑出来后,我们可以单独再画出它们的动量大小(magnitude)的分布。实际上,在ntuple数据里,只需要一条类似如下命令:
ntuple->Draw("sqrt(px*px+py*py+pz*pz)","E>5 && sqrt(x*x+y*y+z*z)<3 && (px*px_sun+py*py_sun+pz*pz_sun)>0.9")
也就是说,在命令里也可以做一些简单的计算。
我们也可以快速扫描数据,打印出感兴趣的事例编码:
ntuple->Scan("eventID","E>5 && sqrt(x*x+y*y+z*z)<3 && (px*px_sun+py*py_sun+pz*pz_sun)>0.9")
初学 Topic3: TTree
ntuple可以看成是简化的TTree。TTree经常用来保存探测器记录的event。
在这里的例子中,我们使用ROOT自带的随机数生成器(TRandom),来生成1000条“假的”人口信息(因为我没有那些数据)。我们假设这个样本里人的年龄平均值为41岁(从前面的真实数据推测),满足高斯分布。男女性别的变量gender满足概率为0.5的二项分布(重复1000次实验,每次实验得到0或1的概率均为0.5)。
对这两种情况,我们直接调用TRandom所带的概率生成器,即随机数生成器分别生成满足高斯分布(平均值40岁,方差20岁)和二项分布的随机数。
直男直女占人数的90%,剩下8%为gay和les,2%为双性恋,因此sexOrient满足概率为0.9,0.08,0.02的多项分布。我们可以考虑使用MultiNominal,但可惜的是,ROOT的TRandom并没有包括这个函数,而是需要调用MathMore库里的。为了避免麻烦,在这里我们使用alias方法,通过分段函数来实现:先生成一个满足[0,1]均匀分布的随机数,如果它小于0.9, 那么就是直男直女,取0, 如果在[0.9, 0.98],则取1,等等。
实际上,这种通过模拟来“虚构”数据的方法,称为玩具蒙特卡罗(Toy Monte Carlo),常用来验证数据和理论的合理性。比如将假数据插入到真数据后在让实验人员进行分析,得到结果后再打开盒子(open the box),让他们剔除假数据后再看看是否会产生同样结果,从而来避免对真数据分析时,实验人员会抱有的某种偏见而造成结果偏差。这称为盲分析(blind analysis)。
生成了1000个人的假数据后,我们把它写到普通的txt文件里,创建一组虚构的原始数据(注意,事先要创建一个空的txt文件)。然后再打开,loop the tree,一条条地记录到TTree里。在这里为了省事,我们在写如虚构数据文件的同时写入TTree,保存为root文件。
运行后直接保持。在Linux里,没有消息是最好的消息,表示顺利运行。
使用了前面的bin小技巧,使binCenter为整数0,1
这里几个直方图之间没有关联,各Fill各的。我们来看TTree!
第二个引号里是切割条件。第三个引号的sames,表示下一张Draw的图叠在之前的上面,同时显示统计盒子。如果是same,不显示统计盒子。
来看看数据关联:蕾丝的年龄分布
这里,在Draw的同时,ROOT也自动计算了计数量,表示这里有45人是蕾丝。
用Scan查看哪些编号的人是蕾丝。这里row行数 相当与编号(对应txt数据文件的第几行)
今天就到这里,谢谢!