图表的概念
最基本的数据可视化方法是使用由两个(TGraph)或三个(TGraph2D)数组组成的坐标表示数据的大小或位置,称作 Graph 。这与直方图(Histogram)不同。
ROOT 提供了许多图形类,如TGraph
图表,TGraphErrors
具有对称误差线的图表,TGraph2D
由三个数组 X、Y 和 Z 组成的图表等。
图表的使用
TGraph
支持批量创建和运算,TGraph
的创建方法是:
Copy // 创建了一个名字是name,标题是title,有n个坐标为x,y的点:
TGraph *gr = new TGraph("name", "title", number, x, y)
TGraph* gr = new TGraph("name", "title", number, x, y)
TGraph *gr = new TGraph(number, x, y)
auto gr = new TGraph(number, x, y)
auto gr = new TGraph() // 使用AddPoint()函数添加坐标点
auto dt = new TGraph2D() // 二维图表,其他形式同理
绘制图表前需要先定义坐标数组,然后使用TGraph
构造图形。E.g.
Copy int n = 20;
double x[n], y[n];
for (int i=0; i<n; i++) {
x[i] = i*0.1;
y[i] = 10*sin(x[i]+0.2);
}
auto gr = new TGraph (n, x, y);
也支持使用AddPoint()
函数添加点:
Copy auto gr = new TGraph();
for (int i=0; i<20; i++) gr->AddPoint(i*0.1, 10*sin(i*0.1+0.2));
使用TGraph::Draw()
绘制图形,且有多种绘图选项 可选:
Copy auto gr = new TGraph();
for (int i=0; i<20; i++) gr->AddPoint(i*0.1, 10*sin(i*0.1+0.2));
gr->Draw();
从文件中读取数据
数据的保存格式有很多,常见的是文本格式和 root 格式:
这里要求文件中的坐标中间是空格或者制表符,不可以是逗号。E.g.
Copy int directdata(){
TGraph *gr = new TGraph("mydata.txt");
gr->Draw("AC*"); // 绘出 axis,坐标点以 curve 链接,坐标点形状为 * 型
}
从图表中读取数据
double *px = gr->GetX(); 将gr1图像中点数据以数组形式输出。
ToggleEventStatus()
是 canvas 的成员函数。用于展示指针处的数据状态,打开后,指针所在位置的数据信息会被呈现在面板下方。
高亮模式适用于TGraph(和TH1)
类。 当高亮模式打开时,鼠标在点上的移动将以图形表示。 点将突出显示为“点圆”(由标记对象表示)。 此外,任何高亮显示(点变化)都会发出信号TCanvas::Highlighted()
,允许用户做出反应并调用自己的函数。 为了更好地理解,请参阅教程$ROOTSYS/tutorials/graphs/hlGraph*.C
文件。
属性设置
图表的轴、标题、颜色均可以进行设置,参考章节:
绘图选项
使用以下选项绘制图表,在Draw()
命令中它们不区分大小写,可不间隔的使用多个参数如Draw("APL")
和Draw("A p l")
,均表示绘制包涵坐标轴和坐标点的图,并用直线相连:
Combine with option 'A' it draws invisible axis
当使用柱状图B时,使柱状图从底部开始,否则从0开始
E.g. X轴在上Y轴反转和自动取色
Copy void graphpalettecolor () {
gStyle->SetOptTitle(kFALSE);
gStyle->SetPalette(kSolar);
double x[5] = {1,2,3,4,5};
double y1[5] = {1.0,2.0,1.0,2.5,3.0};
double y2[5] = {1.1,2.1,1.1,2.6,3.1};
double y3[5] = {1.2,2.2,1.2,2.7,3.2};
double y4[5] = {1.3,2.3,1.3,2.8,3.3};
double y5[5] = {1.4,2.4,1.4,2.9,3.4};
TGraph *g1 = new TGraph(5,x,y1); g1->SetTitle("Graph with a red star");
TGraph *g2 = new TGraph(5,x,y2); g2->SetTitle("Graph with a circular marker");
TGraph *g3 = new TGraph(5,x,y3); g3->SetTitle("Graph with an open square marker");
TGraph *g4 = new TGraph(5,x,y4); g4->SetTitle("Graph with a blue star");
TGraph *g5 = new TGraph(5,x,y5); g5->SetTitle("Graph with a full square marker");
g1->SetLineWidth(3); g1->SetMarkerColor(kRed);
g2->SetLineWidth(3); g2->SetMarkerStyle(kCircle);
g3->SetLineWidth(3); g3->SetMarkerStyle(kOpenSquare);
g4->SetLineWidth(3); g4->SetMarkerColor(kBlue);
g5->SetLineWidth(3); g5->SetMarkerStyle(kFullSquare);
g1->Draw("CA*x+ry PLC PFC");
g2->Draw("PCx+ry PLC PFC");
g3->Draw("PC PLC PFC");
g4->Draw("*C PLC PFC");
g5->Draw("PC PLC PFC");
gPad->BuildLegend();
}
成果保存
将分析成果保存并反复展示。root 格式支持保存数据,也支持保存分析图片,而且图片保存为 root 格式后还可以反复打开并进行进一步的美化工作,十分便捷。
Save()
SaveAs()函数,支持
Write()函数
Print()
E.g. 将多个画布与图片连续保存到一个 PDF 文件中
Copy {
TCanvas* canvas = new TCanvas("canvas");
TH1F* histo = new TH1F("histo","test 1",10,0.,10.);
histo->SetFillColor(2);
histo->Fill(2.);
histo->Draw();
canvas->Print("plots.pdf(","Title:One bin filled");
histo->Fill(4.);
histo->Draw();
canvas->Print("plots.pdf","Title:Two bins filled");
histo->Fill(6.);
histo->Draw();
canvas->Print("plots.pdf","Title:Three bins filled");
histo->Fill(8.);
histo->Draw();
canvas->Print("plots.pdf","Title:Four bins filled");
histo->Fill(8.);
histo->Draw();
canvas->Print("plots.pdf)","Title:The fourth bin content is 2");
}
误差与拟合
二维图表
使用符合标准的数据可以绘制二维图表。E.g.
Copy {
auto c = new TCanvas("c","Graph2D example",0,0,700,600); // 画布距离左上角的距离(0,0)
double x, y, z, P = 6.;
int np = 200;
auto dt = new TGraph2D();
auto r = new TRandom();
for (int N=0; N<np; N++) {
x = 2*P*(r->Rndm(N))-P;
y = 2*P*(r->Rndm(N))-P;
z = (sin(x)/x)*(sin(y)/y)+0.2;
dt->SetPoint(N,x,y,z);
}
dt->Draw("tri1 p0");
}
绘图选项
The Delaunay triangles are drawn using filled area. An hidden surface drawing technique is used. The surface is painted with the current fill area color. The edges of each triangles are painted with the current line color.
The Delaunay triangles are drawn as wire frame.
The Delaunay triangles are painted with color levels. The edges of each triangles are painted with the current line color.
The Delaunay triangles are painted with color levels.
Draw a marker at each vertex.
Draw a circle at each vertex. Each circle background is white.
Draw a marker at each vertex. The color of each marker is defined according to its Z position.
Draw a contour plot using Delaunay triangles.
极坐标 TGraphPolar
极坐标TGraphPolar()
的绘图选项有:
E.g. 极坐标例子
Copy {
auto c46 = new TCanvas("c46","c46",500,500);
auto grP1 = new TGraphPolar();
grP1->SetTitle("TGraphPolar example");
grP1->SetPoint(0, (1*TMath::Pi())/4., 0.05);
grP1->SetPoint(1, (2*TMath::Pi())/4., 0.10);
grP1->SetPoint(2, (3*TMath::Pi())/4., 0.15);
grP1->SetPoint(3, (4*TMath::Pi())/4., 0.20);
grP1->SetPoint(4, (5*TMath::Pi())/4., 0.25);
grP1->SetPoint(5, (6*TMath::Pi())/4., 0.30);
grP1->SetPoint(6, (7*TMath::Pi())/4., 0.35);
grP1->SetPoint(7, (8*TMath::Pi())/4., 0.40);
grP1->SetMarkerStyle(20);
grP1->SetMarkerSize(1.);
grP1->SetMarkerColor(4);
grP1->SetLineColor(4);
grP1->Draw("ALP");
// Update, otherwise GetPolargram returns 0
c46->Update();
grP1->GetPolargram()->SetToRadian();
}
饼状图 TPie
多图绘法
root 支持在一张 canvas 上绘制多幅图表或者直方图。
独立绘制
使用TCanvas::Divide()
函数将 Canvas 分割为不同的 Pad,例如将名为 c1 的 canvas 分为 2 列 3 行写作:c1->Divide(2,3)
,然后使用c1->cd()
分别控制不同的 pad,pad 的名称从左向右,从上至下分别是1,2,……,例如可以在 pad1 上绘制 histogram,在 pad2 上绘制 graph。
共轴绘制
最简单的共轴画法是在TCanvas::Divide()
函数中添加参数,例如c1->Divide(2,3,0)
,但当要进行一些美化工作的时候,就会发现它们的标题大小,marker 大小不再能通过统一的数值控制,这非常影响美观。
所以这里有一个稍微复杂的解决方法,能够使它们的大小保持一致,仅供参考:
CanvasPartition https://root.cern.ch/doc/master/canvas2_8C.html
Copy
void CanvasPartition(TCanvas *C,const Int_t Nx = 2,const Int_t Ny = 2,
Float_t lMargin = 0.15, Float_t rMargin = 0.05,
Float_t bMargin = 0.15, Float_t tMargin = 0.05);
double XtoPad(double x);
double YtoPad(double x);
void canvas2()
{
gStyle->SetOptStat(0);
auto C = (TCanvas*) gROOT->FindObject("C");
if (C) delete C;
C = new TCanvas("C","canvas",1024,640);
C->SetFillStyle(4000);
// Number of PADS
const Int_t Nx = 5;
const Int_t Ny = 5;
// Margins
Float_t lMargin = 0.12;
Float_t rMargin = 0.05;
Float_t bMargin = 0.15;
Float_t tMargin = 0.05;
// Canvas setup
CanvasPartition(C,Nx,Ny,lMargin,rMargin,bMargin,tMargin);
// Dummy histogram.
auto h = (TH1F*) gROOT->FindObject("histo");
if (h) delete h;
h = new TH1F("histo","",100,-5.0,5.0);
h->FillRandom("gaus",10000);
h->GetXaxis()->SetTitle("x axis");
h->GetYaxis()->SetTitle("y axis");
TPad *pad[Nx][Ny];
for (Int_t i = 0; i < Nx; i++) {
for (Int_t j = 0; j < Ny; j++) {
C->cd(0);
// Get the pads previously created.
pad[i][j] = (TPad*) C->FindObject(TString::Format("pad_%d_%d",i,j).Data());
pad[i][j]->Draw();
pad[i][j]->SetFillStyle(4000);
pad[i][j]->SetFrameFillStyle(4000);
pad[i][j]->cd();
// Size factors
Float_t xFactor = pad[0][0]->GetAbsWNDC()/pad[i][j]->GetAbsWNDC();
Float_t yFactor = pad[0][0]->GetAbsHNDC()/pad[i][j]->GetAbsHNDC();
TH1F *hFrame = (TH1F*) h->Clone(TString::Format("h_%d_%d",i,j).Data());
// y axis range
hFrame->SetMinimum(0.0001); // do not show 0
hFrame->SetMaximum(1.2*h->GetMaximum());
// Format for y axis
hFrame->GetYaxis()->SetLabelFont(43);
hFrame->GetYaxis()->SetLabelSize(16);
hFrame->GetYaxis()->SetLabelOffset(0.02);
hFrame->GetYaxis()->SetTitleFont(43);
hFrame->GetYaxis()->SetTitleSize(16);
hFrame->GetYaxis()->SetTitleOffset(2);
hFrame->GetYaxis()->CenterTitle();
hFrame->GetYaxis()->SetNdivisions(505);
// TICKS Y Axis
hFrame->GetYaxis()->SetTickLength(xFactor*0.04/yFactor);
// Format for x axis
hFrame->GetXaxis()->SetLabelFont(43);
hFrame->GetXaxis()->SetLabelSize(16);
hFrame->GetXaxis()->SetLabelOffset(0.02);
hFrame->GetXaxis()->SetTitleFont(43);
hFrame->GetXaxis()->SetTitleSize(16);
hFrame->GetXaxis()->SetTitleOffset(1);
hFrame->GetXaxis()->CenterTitle();
hFrame->GetXaxis()->SetNdivisions(505);
// TICKS X Axis
hFrame->GetXaxis()->SetTickLength(yFactor*0.06/xFactor);
// Draw cloned histogram with individual settings
hFrame->Draw();
TText text;
text.SetTextAlign(31);
text.SetTextFont(43);
text.SetTextSize(10);
text.DrawTextNDC(XtoPad(0.9), YtoPad(0.8), gPad->GetName());
}
}
C->cd();
}
void CanvasPartition(TCanvas *C,const Int_t Nx,const Int_t Ny,
Float_t lMargin, Float_t rMargin,
Float_t bMargin, Float_t tMargin)
{
if (!C) return;
// Setup Pad layout:
Float_t vSpacing = 0.0;
Float_t vStep = (1.- bMargin - tMargin - (Ny-1) * vSpacing) / Ny;
Float_t hSpacing = 0.0;
Float_t hStep = (1.- lMargin - rMargin - (Nx-1) * hSpacing) / Nx;
Float_t vposd,vposu,vmard,vmaru,vfactor;
Float_t hposl,hposr,hmarl,hmarr,hfactor;
for (Int_t i=0;i<Nx;i++) {
if (i==0) {
hposl = 0.0;
hposr = lMargin + hStep;
hfactor = hposr-hposl;
hmarl = lMargin / hfactor;
hmarr = 0.0;
} else if (i == Nx-1) {
hposl = hposr + hSpacing;
hposr = hposl + hStep + rMargin;
hfactor = hposr-hposl;
hmarl = 0.0;
hmarr = rMargin / (hposr-hposl);
} else {
hposl = hposr + hSpacing;
hposr = hposl + hStep;
hfactor = hposr-hposl;
hmarl = 0.0;
hmarr = 0.0;
}
for (Int_t j=0;j<Ny;j++) {
if (j==0) {
vposd = 0.0;
vposu = bMargin + vStep;
vfactor = vposu-vposd;
vmard = bMargin / vfactor;
vmaru = 0.0;
} else if (j == Ny-1) {
vposd = vposu + vSpacing;
vposu = vposd + vStep + tMargin;
vfactor = vposu-vposd;
vmard = 0.0;
vmaru = tMargin / (vposu-vposd);
} else {
vposd = vposu + vSpacing;
vposu = vposd + vStep;
vfactor = vposu-vposd;
vmard = 0.0;
vmaru = 0.0;
}
C->cd(0);
auto name = TString::Format("pad_%d_%d",i,j);
auto pad = (TPad*) C->FindObject(name.Data());
if (pad) delete pad;
pad = new TPad(name.Data(),"",hposl,vposd,hposr,vposu);
pad->SetLeftMargin(hmarl);
pad->SetRightMargin(hmarr);
pad->SetBottomMargin(vmard);
pad->SetTopMargin(vmaru);
pad->SetFrameBorderMode(0);
pad->SetBorderMode(0);
pad->SetBorderSize(0);
pad->Draw();
}
}
}
double XtoPad(double x)
{
double xl,yl,xu,yu;
gPad->GetPadPar(xl,yl,xu,yu);
double pw = xu-xl;
double lm = gPad->GetLeftMargin();
double rm = gPad->GetRightMargin();
double fw = pw-pw*lm-pw*rm;
return (x*fw+pw*lm)/pw;
}
double YtoPad(double y)
{
double xl,yl,xu,yu;
gPad->GetPadPar(xl,yl,xu,yu);
double ph = yu-yl;
double tm = gPad->GetTopMargin();
double bm = gPad->GetBottomMargin();
double fh = ph-ph*bm-ph*tm;
return (y*fh+bm*ph)/ph;
}
重叠绘制
将多个 graph 绘制在一张 pad 上,只需要在TGraph::Draw()
函数中使用same
参数即可。
合并绘制
TMultiGraph::TMultiGraph()
,函数支持将一组图作为单个实体进行操作。且绘制时 X 轴和 Y 轴范围会自动计算,因此所有图形都将可见。TMultiGraph::Add
()
用于将新图表添加到列表中。E.g.
Copy TGraph *gr1 = new TGraph(...
TGraphErrors *gr2 = new TGraphErrors(...
TMultiGraph *mg = new TMultiGraph();
mg->Add(gr1,"lp");
mg->Add(gr2,"cp");
mg->Draw("a");
标题、颜色、统计信息均可使用TMultiGraph
类中的成员函数,用法与 graph 一致。
单个 graph 通过mg->GetListOfGraphs()->GetEntries();
方式检索。
多页绘制
将graph保存到多页PDF中。
Copy {
TCanvas* canvas = new TCanvas ("canvas") ;
TH1F* histo = new THIF("histo"c, "test 1", 10, 0., 10.) ;
histo->SetFillColor (2); histo->Fil1(2.); histo->Draw();
canvas->Print("plots.pdf(","Title:One bin filled"); histo->Fi11(4.); histo->Draw();
canvas->Print "plots.pdf", "Title:Two bins filled"); histo->Fi11(6.);
histo->Draw();
canvas->Print ("plots.pdf", "Title: Three bins filled");
histo->Fill (8.);
histo->Draw();
canvas->Print("plots.pdf", "Title:Four bins filled");
histo->Fi11(8.);
histo->Draw():
canvas->Print ("plots.pdf)", "Title:The fourth bin content is 2");
}
图例
参考
https://root.cern/doc/master/classTGaxis.html
https://root.cern/doc/master/classTGraphPainter.html
https://root.cern.ch/doc/master/classTHistPainter.html
Last updated 5 months ago