動(dòng)高效出圖實(shí)踐)
簡(jiǎn)介plotIt-master是一個(gè)面向C研究人員與開發(fā)者的ROOT直方圖繪制工具源碼包通過(guò)簡(jiǎn)潔API自動(dòng)完成直方圖創(chuàng)建、填充與繪圖幫助用戶避開繁瑣的底層代碼更專注于數(shù)據(jù)分析本身。資源共17個(gè)文件主要包括6個(gè)C頭文件與3個(gè)源文件分別承擔(dān)接口定義與核心實(shí)現(xiàn)另有Shell環(huán)境配置腳本、Python輔助腳本、Makefile構(gòu)建文件及YAML示例配置壓縮包整體僅22KB。已有256人學(xué)習(xí)下載。解壓后即可查看README與示例通過(guò)makefile編譯集成快速生成支持自定義分箱、誤差棒、軸標(biāo)簽與圖例的單維/多維直方圖配套的example.yml與構(gòu)建腳本能顯著降低環(huán)境配置門檻適合高能物理、數(shù)據(jù)科學(xué)等領(lǐng)域需要頻繁繪制ROOT直方圖的初學(xué)者與資深用戶。1. plotIt 是什么在 ROOT 繪圖這條路上把重復(fù)勞動(dòng)交給配置文件如果你用 ROOT 畫過(guò)上百?gòu)堉狈綀D應(yīng)該記得那種改一個(gè)變量就要改一遍宏的滋味。真正花時(shí)間的不是畫圖而是讓每張圖都符合統(tǒng)一規(guī)范字體、坐標(biāo)軸、圖例、logY、比值面板。plotIt 就是把這一層重復(fù)勞動(dòng)抽出來(lái)的實(shí)用程序它讀取 ROOT 文件里已經(jīng)填好的 TH1按一份 YAML 配置決定畫什么、怎么疊、開不開 logY、帶不帶比值面板一條命令批量輸出 PDF/PNG。適合正在做粒子物理數(shù)據(jù)分析的人也適合被批量出圖拖后腿的團(tuán)隊(duì)。注意這里說(shuō)的直方圖是 ROOT 框架的統(tǒng)計(jì)分布 TH1不是圖像處理里做直方圖均衡化的那個(gè)直方圖root 是 CERN 的軟件框架不是 Linux root 賬號(hào)。2. 從源碼裝好并用最小配置跑出第一張圖依賴、demo 數(shù)據(jù)與第一份 YAML2.1 依賴檢查和編譯ROOT 6 環(huán)境先就位再談其他先把話說(shuō)清楚plotIt 不是獨(dú)立畫圖軟件它要騎在 ROOT 肩膀上畫布、直方圖、字體、PDF 輸出全走 ROOT 的 C 核心庫(kù)。所以第一步不是急著下源碼而是確認(rèn) ROOT 環(huán)境能用。source /opt/root/bin/thisroot.sh root-config --version which cmake第二行會(huì)打印 ROOT 版本比如 6.24.06。后面接 CMake。thisroot.sh的路徑取決于你裝 ROOT 的方式可能不在/opt/root下面。只要root-config能被 which 找到編譯時(shí)就能正確拿到 ROOT 的頭文件和庫(kù)文件。如果前兩行就卡住先回去折騰 ROOT 安裝別急著編譯 plotIt——我見過(guò)最長(zhǎng)的一次編譯翻車現(xiàn)場(chǎng)最后發(fā)現(xiàn)只是 ROOT 沒(méi) source。源碼拿到本地后我習(xí)慣用 CMake 編譯因?yàn)榭梢皂樖种付ò惭b前綴把工具裝進(jìn)用戶目錄不污染系統(tǒng)。cd plotIt mkdir -p build cd build cmake -DCMAKE_INSTALL_PREFIX$HOME/local .. make -j$(nproc) make install hash -r which plotIt第一行進(jìn)入源碼目錄第二行新建獨(dú)立構(gòu)建目錄這是 CMake 的常規(guī)用法避免源碼目錄里堆滿中間文件。-DCMAKE_INSTALL_PREFIX讓 make install 把可執(zhí)行文件放到$HOME/local/bin以后卸載直接刪目錄就行。make -j$(nproc)用滿 CPU 核心對(duì)這種小工具基本半分鐘就編完。如果 cmake 報(bào) yaml-cpp 找不到說(shuō)明系統(tǒng)缺 C 的 YAML 解析庫(kù)Debian/Ubuntu 上裝libyaml-cpp-dev即可。有些版本還依賴 boost 的 filesystem 庫(kù)報(bào)錯(cuò)信息會(huì)寫清楚按包名補(bǔ)上就行。裝完用which plotIt確認(rèn)可執(zhí)行文件已經(jīng)出現(xiàn)如果 which 沒(méi)找到檢查$HOME/local/bin是否在 PATH 里或者直接用全路徑調(diào)用。提示不同發(fā)行版本的 plotIt 編譯流程有細(xì)微差別有的源碼里干脆只帶 Makefile。動(dòng)手前先看倉(cāng)庫(kù) README確認(rèn)該用 CMake 還是直接 make能省掉無(wú)謂的折騰。2.2 準(zhǔn)備一棵最小輸入直方圖用 PyROOT 生成 demo ROOT 文件plotIt 只畫“已經(jīng)存在”的直方圖它不負(fù)責(zé)填數(shù)。真實(shí)工作流里這些直方圖是分析作業(yè)跑完寫進(jìn) ROOT 文件的為了練習(xí)可以用 PyROOT 造兩個(gè)假的——一個(gè)代表數(shù)據(jù)一個(gè)代表 MC 本底。import ROOT f ROOT.TFile(data.root, RECREATE) h ROOT.TH1F(MET, MET, 20, 0, 200) h.FillRandom(gaus, 10000) h.Write() f.Close()TH1F五個(gè)參數(shù)依次是直方圖名字、標(biāo)題、分箱數(shù)、下界、上界。真正分析里這一句會(huì)被事件選擇代碼替代但名字這個(gè)參數(shù)要特別在意plotIt 靠它去 ROOT 文件里定位直方圖起名時(shí)最好帶完整語(yǔ)義比如jet1_pt、muon_eta而不是h1。FillRandom(gaus, 10000)是偷懶式填數(shù)讓直方圖有個(gè)高斯形狀Write()把它寫進(jìn) TFileClose()落盤。MC 那邊稍微多兩步假裝做過(guò)一次歸一化import ROOT f ROOT.TFile(mc.root, RECREATE) h ROOT.TH1F(MET, MET, 20, 0, 200) h.FillRandom(gaus, 12000) h.Scale(0.8) h.Write() f.Close()Scale(0.8)相當(dāng)于給 MC 乘以權(quán)重讓它的積分和 Data 大致匹配。真實(shí)分析里這個(gè)權(quán)重來(lái)自截面、亮度、篩選效率的乘積不會(huì)在繪圖工具里手動(dòng)亂乘我這里只是讓兩張圖疊在一起時(shí)不至于比例離譜。注意直方圖名字是“MET”后面配置文件里的 histo 字段就要寫“MET”對(duì)不上會(huì)直接報(bào)找不到對(duì)象。2.3 第一個(gè)真配置文件與第一條命令數(shù)據(jù)和一個(gè) MC 文件都備齊后寫第一份配置。文件名叫first.ymldatasets: - name: Data file: data.root histo: MET color: 1 marker: 20 - name: MC file: mc.root histo: MET color: 2 stack: 1 layout: title: MET spectrum lumi: 35.9 energy: 13 logY: false ratio: false format: PDF結(jié)構(gòu)上配置分成datasets和layout兩段。datasets 里每一行描述一個(gè)要畫的直方圖name 是顯示在圖例上的名字file 是 ROOT 文件路徑histo 是文件里的直方圖名字color 是 ROOT 調(diào)色板索引1 黑、2 紅、3 綠這樣排stack: 1 表示參與堆疊。layout 段控制整個(gè)畫布title 出現(xiàn)在圖頂lumi 和 energy 會(huì)渲染成35.9 fb^{-1}, 13 TeV的標(biāo)頭logY 控制縱軸是否對(duì)數(shù)ratio 控制要不要數(shù)據(jù)/MC 比值面板format 指定輸出格式。運(yùn)行命令只有一行plotIt -u first.yml ls -la-u在我用的版本里是更新模式作用是允許重復(fù)運(yùn)行覆蓋已有輸出而不是每跑一次就在文件名后面加序號(hào)。跑完ls看當(dāng)前目錄應(yīng)該多出一個(gè) PDF。如果報(bào)error while loading shared libraries: libCore.so不是 plotIt 的問(wèn)題而是當(dāng)前 shell 沒(méi) source ROOT 環(huán)境回到 2.1 第一行即可。這張圖通常不會(huì)好看但沒(méi)關(guān)系。plotIt 的價(jià)值不是讓單張圖更美而是讓你把這份 YAML 當(dāng)作模板換變量名、換文件路徑批量產(chǎn)出統(tǒng)一規(guī)范的圖。它對(duì)標(biāo)的是手寫TCanvas、Draw、SaveAs那一堆樣板代碼贏在批量不贏在單張。不同版本對(duì) YAML 字段的支持有差異有的版本叫l(wèi)ogY有的叫l(wèi)og寫配置前打開倉(cāng)庫(kù)自帶 example 看一眼字段以它為準(zhǔn)。3. 把配置文件寫厚datasets、layout 與輸出組織3.1 datasets 塊數(shù)據(jù)、本底、信號(hào)的身份要分清楚只有兩個(gè) dataset 的配置太單薄。真實(shí)分析里至少有 Data、數(shù)個(gè)本底、可能還有信號(hào)顏色、堆疊行為、分組方式都不一樣。把 datasets 段展開datasets: - name: Data file: data.root histo: MET color: 1 marker: 20 group: data - name: QCD file: qcd.root histo: MET color: 2 stack: 1 group: bkg - name: Wjets file: wjets.root histo: MET color: 3 stack: 1 group: bkg比 2.3 多出來(lái)的關(guān)鍵字段是group。它把多個(gè) dataset 劃進(jìn)同一個(gè)集合后續(xù)想整組歸一化、整組隱藏時(shí)不用一個(gè)個(gè)列名字。我的習(xí)慣是數(shù)據(jù)歸data本底歸bkg有信號(hào)樣本就單獨(dú)開sig組??磁渲脮r(shí)哪一堆是數(shù)據(jù)、哪一堆是陪跑本底一眼讀出來(lái)。stack: 1決定這個(gè) dataset 是否參與堆疊。數(shù)據(jù)不堆疊用 marker 畫點(diǎn)MC 本底堆疊成色塊彼此累加。堆疊順序一般按配置文件里的出現(xiàn)順序第一個(gè)寫在最底層。如果某個(gè)本底橫跨整個(gè)能譜且量級(jí)很大放在最底層是好習(xí)慣否則后面的信號(hào)容易被壓住。一個(gè)細(xì)節(jié)dataset 的 name 直接進(jìn)圖例。Wjets這種帶加號(hào)的字符串YAML 里最好加引號(hào)包住部分解析器會(huì)把它當(dāng)成特殊 tag 而出錯(cuò)。組里的規(guī)矩是所有 name 一律加引號(hào)。dataset 字段名也有版本差異我用的版本file是單個(gè)路徑有的工具用files接列表并自動(dòng)相加。如果支持列表同本底拆成多個(gè)跑批文件時(shí)就不用先合并直方圖直接列出來(lái)由程序加總。3.2 layout 塊坐標(biāo)軸、標(biāo)題、logY 一個(gè)都不能少datasets 決定“畫什么”layout 決定“畫在什么樣的畫布上”。這一塊我通常按下面的表逐項(xiàng)核對(duì)字段作用常用值title圖頂大標(biāo)題Inclusive jet p_{T}xLabel橫軸標(biāo)簽p_{T} [GeV]yLabel縱軸標(biāo)簽常含分箱寬度Events / GeVlumi亮度值顯示在圖例上方35.9energy對(duì)撞能量13logY縱軸是否對(duì)數(shù)坐標(biāo)false / truexRange橫軸顯示范圍[0, 200]yRange縱軸顯示范圍留給自動(dòng)ratio是否需要比值面板false / truegrid畫布網(wǎng)格線falseformat輸出格式PDFwatermark圖角水印Internallayout 段示例layout: title: MET spectrum xLabel: E_{T}^{miss} [GeV] yLabel: Events / 10 GeV lumi: 35.9 energy: 13 logY: true xRange: [0, 200] ratio: false grid: false format: PDFlogY 的經(jīng)驗(yàn)法則縱軸數(shù)據(jù)跨幾個(gè)量級(jí)就開最多和最少差不到一個(gè)數(shù)量級(jí)就別開。開 logY 后 yRange 仍是線性坐標(biāo)里的數(shù)值范圍但視覺(jué)被對(duì)數(shù)壓縮新手常在這里翻車把 yRange 下界設(shè)成 0對(duì)數(shù)軸直接畫不出來(lái)。yLabel 里的分箱寬度是粒子物理出圖的禮貌問(wèn)題寫成Events / 10 GeV代表每分箱寬度歸一評(píng)審看著更順眼。plotIt 不會(huì)幫你自動(dòng)算要在 yLabel 里手寫清楚。xLabel 里的E_{T}^{miss}是 ROOT 的數(shù)學(xué)文本語(yǔ)法能渲染上標(biāo)注意字符串里的反斜杠轉(zhuǎn)義。watermark 字段平時(shí)不顯眼但內(nèi)部評(píng)審時(shí)非常有用。同一張圖帶 Internal 水印的發(fā)組會(huì)材料不帶水印的進(jìn)論文同一個(gè)配置只改一個(gè)字段。這個(gè)習(xí)慣幫我擋了不少次“這張圖能不能外發(fā)”的追問(wèn)。3.3 一份配置產(chǎn)出多種格式輸出目錄與命名管理layout 里的format多數(shù)版本支持一次輸出多個(gè)格式寫法有的用逗號(hào)有的用列表。我常用layout: format: [PDF, PNG] outputDir: plots_v1一次跑完P(guān)DF 寫論文插 LaTeXPNG 貼聊天記錄和會(huì)議紀(jì)要。outputDir如果版本支持所有產(chǎn)物集中在目錄里清理備份都方便不支持的版本我用 shell 重定向一樣能達(dá)到目的。真正值得提前想清楚的是輸出文件命名。常見邏輯是把 name 和 histo 拼一下比如Data_MET.pdf、QCD_MET.pdf。如果兩個(gè)配置文件畫同一個(gè)直方圖、同一個(gè)數(shù)據(jù)集名字后跑的會(huì)覆蓋先跑的——這是最安靜的坑不報(bào)錯(cuò)文件悄悄變舊。我的做法是每個(gè)物理分析建獨(dú)立輸出目錄目錄名帶日期和版本比如plots_v1、plots_v2_fixBinning。目錄一多誰(shuí)哪天跑過(guò)哪版圖翻名字就知道。輸出命名還有一個(gè)實(shí)踐直方圖名字盡量全局唯一。同一個(gè) ROOT 文件里同時(shí)存在MET和h_MET配置里寫錯(cuò)名不會(huì)報(bào)重名而是畫錯(cuò)對(duì)象。我在分析框架里填直方圖時(shí)就統(tǒng)一用varname_region_suffix格式把命名習(xí)慣前置到填數(shù)階段比在繪圖階段補(bǔ)救便宜得多。4. 畫出版級(jí)效果比值面板、誤差帶與多變量循環(huán)出圖4.1 打開 ratio 面板數(shù)據(jù)/MC 比值的兩級(jí)臺(tái)階粒子物理內(nèi)部評(píng)審有個(gè)潛規(guī)則光看堆疊圖不算數(shù)底下必須掛一條數(shù)據(jù)/MC 比值線。堆疊圖里的差距會(huì)被顏色和對(duì)數(shù)軸掩蓋比值面板能直接暴露哪個(gè) bin 偏離模型。很多工具不幫你做這一步plotIt 這類程序通常是一個(gè)開關(guān)的事。datasets: - name: Data file: data.root histo: MET color: 1 marker: 20 - name: QCD file: qcd.root histo: MET color: 2 stack: 1 - name: Wjets file: wjets.root histo: MET color: 3 stack: 1 layout: title: MET spectrum lumi: 35.9 energy: 13 ratio: true ratioRange: [0.9, 1.1] yLabel: Events format: PDFratio: true會(huì)把畫布切成上下兩個(gè) TPad上 pad 畫堆疊與數(shù)據(jù)下 pad 畫比值。第二級(jí)臺(tái)階是下 pad 的縱軸范圍。我用的版本支持ratioRange直接寫范圍如果字段不識(shí)別去倉(cāng)庫(kù) README 里搜 ratio看它實(shí)際叫什么。下 pad 用自動(dòng)范圍時(shí)比值圖的零點(diǎn)會(huì)被拉得很低看起來(lái)到處都是偏差其實(shí)只是顯示問(wèn)題。范圍鎖在 0.9 到 1.1 是常見做法統(tǒng)計(jì)量小就放寬到 0.8 到 1.2不丟人。ratio 面板有個(gè)前提MC 必須先歸一化到數(shù)據(jù)。如果 MC 積分只有數(shù)據(jù)六成比值面板整體漂在 0.6 附近看不出任何 bin 的欠擬合。開 ratio 之前第一件事是把 MC 權(quán)重流程檢查一遍。4.2 系統(tǒng)誤差帶誤差要提前算好畫圖只是最后一步系統(tǒng)誤差帶是出圖寫論文繞不開的一環(huán)。先說(shuō)原理繪圖程序里的誤差帶本質(zhì)是一條帶上下界的曲線它不負(fù)責(zé)計(jì)算誤差從哪來(lái)。真實(shí)流程是在分析框架里對(duì)每個(gè)系統(tǒng)源做一次重填直方圖累加得到每個(gè) bin 的總上下偏差存成 TH1 或 TGraphAsymmErrors繪圖時(shí)貼到對(duì)應(yīng)的 MC 直方圖上。我用的版本在 dataset 上支持這樣的寫法- name: QCD file: mc.root histo: MET stack: 1 errorBandUp: MET_sys_up errorBandDown: MET_sys_downerrorBandUp和errorBandDown指到 ROOT 文件里的另外兩條直方圖繪圖時(shí)在堆疊圖上畫一個(gè)淡色帶子。不同版本的字段名可能不同有的叫errorBand有的要求先把上下界合成 TGraphAsymmErrors 再指過(guò)去。動(dòng)手前翻倉(cāng)庫(kù) example把字段認(rèn)準(zhǔn)這是我被各種版本差異坑過(guò)之后的固定動(dòng)作。用 PyROOT 造誤差帶示意文件import ROOT f ROOT.TFile(mc.root, UPDATE) h f.Get(MET) h.Sumw2() sig_up h.Clone(MET_sys_up) sig_down h.Clone(MET_sys_down) for b in range(1, sig_up.GetNbinsX() 1): sig_up.SetBinContent(b, h.GetBinContent(b) * 1.05) sig_down.SetBinContent(b, h.GetBinContent(b) * 0.95) sig_up.Write() sig_down.Write() f.Close()關(guān)鍵在Sumw2()。它讓 ROOT 給直方圖維護(hù)誤差平方和數(shù)組之后的統(tǒng)計(jì)誤差才有意義。很多人畫誤差帶發(fā)現(xiàn)帶子是零絕大多數(shù)就是忘了 Sumw2。Clone復(fù)制直方圖再逐 bin 改 content 作為上下界。真實(shí)分析里誤差源有十幾二十個(gè)逐源掃完再累加這里用 5% 只是演示“直方圖可以作為誤差帶輸入”。誤差帶的質(zhì)量取決于輸入不取決于畫法別在繪圖階段臨時(shí)補(bǔ)誤差。4.3 多變量批量繪制一套 datasets 配多個(gè) histo前面所有例子都只畫一個(gè)變量實(shí)際分析要出的是成批的圖。多數(shù)版本的 plotItdataset 里的 histo 字段是單數(shù)形式不支持?jǐn)?shù)組。我應(yīng)對(duì)的辦法是datasets 和 layout 寫成模板變量名用占位符外面套 shell 循環(huán)。模板template.ymldatasets: - name: Data file: data.root histo: HISTONAME color: 1 marker: 20 - name: QCD file: qcd.root histo: HISTONAME color: 2 stack: 1 layout: title: HISTONAME distribution lumi: 35.9 energy: 13 ratio: true format: PDF循環(huán)腳本for var in MET pt_jet n_jet; do sed s/HISTONAME/$var/g template.yml /tmp/cfg_${var}.yml plotIt -u /tmp/cfg_${var}.yml donesed 把模板里所有HISTONAME替換成當(dāng)前變量名生成臨時(shí)配置再調(diào) plotIt。這里有個(gè)細(xì)節(jié)直方圖名字不帶擴(kuò)展名如果同名直方圖在 Data 和 MC 文件里都存在替換一次全部命中輸出 PDF 文件名帶上變量名不會(huì)互相覆蓋。這套循環(huán)看起來(lái)簡(jiǎn)單但從“手動(dòng)改一個(gè)變量畫一張”升級(jí)到“一個(gè)循環(huán)畫完關(guān)鍵變量”是工作方式的分水嶺。我后來(lái)封裝成腳本入?yún)⑹亲兞苛斜沓鰠⑹且慌?PDF/PNG。新增變量時(shí)只要保證分析代碼真的把直方圖 Fill 進(jìn)去其余全自動(dòng)。循環(huán)能跑通的前提是直方圖命名規(guī)整。如果變量叫bkg_pt但配置里寫pt循環(huán)會(huì)安靜地跳過(guò)直到查圖時(shí)才發(fā)現(xiàn)少一張。所以占位符最好帶路徑前綴比如jets/HISTONAME與 ROOT 文件內(nèi)部目錄嚴(yán)格一致。5. 避坑五類最常見的崩潰與錯(cuò)圖陷阱5.1 ROOT 版本不一致本地能編服務(wù)器上崩現(xiàn)象同一份配置在實(shí)驗(yàn)室服務(wù)器畫圖正常在自己筆記本上一跑就 segmentation fault或者圖能出圖例里的希臘字母全變方框。原因ROOT 版本跨度大時(shí)繪圖后端 API 有破壞性變更編譯期和運(yùn)行期版本不一致就翻車系統(tǒng)缺字體是方框的元兇。另一個(gè)高頻現(xiàn)場(chǎng)是 Python 側(cè)包裝的 plotIt 與 PyROOT 綁定版本不匹配加載時(shí)就崩。解決把服務(wù)器root-config --version記下來(lái)本地裝同一個(gè)大版本6.24 對(duì) 6.24 絕大多數(shù)情況下沒(méi)大礙。字體問(wèn)題優(yōu)先用 ROOT 內(nèi)置編號(hào)字體配置里把 font 設(shè)成 42即 Helvetica 內(nèi)置編號(hào)不依賴系統(tǒng)字體環(huán)境。我之后固定了一套環(huán)境清單ROOT 版本、編譯器版本、yaml-cpp 版本寫成一行的記錄換機(jī)器先對(duì)齊再跑圖。5.2 YAML 縮進(jìn)錯(cuò)誤四個(gè)空格引發(fā)的數(shù)據(jù)失蹤現(xiàn)象plotIt 能啟動(dòng)但圖里少了一個(gè) dataset或者所有 MC 顏色一樣堆疊沒(méi)生效。原因YAML 列表項(xiàng)的屬性沒(méi)有對(duì)齊。最常見的是stack或group寫到 dataset 的同級(jí)而不是縮進(jìn)的子級(jí)解析器把它當(dāng)成另一條記錄。Tab 和空格混用是同族問(wèn)題YAML 里 Tab 是非法字符很多編輯器默認(rèn)縮進(jìn)用 Tab復(fù)制進(jìn)配置就出鬼。解決統(tǒng)一用兩個(gè)空格縮進(jìn)編輯器開啟顯示空白字符。不確定層級(jí)時(shí)對(duì)著 example 逐行數(shù)空格。壞例子- name: QCD file: qcd.root histo: MET color: 2 stack: 1stack: 1少兩格QCD 的 stack 沒(méi)生效。和 file、histo 對(duì)齊就好了- name: QCD file: qcd.root histo: MET color: 2 stack: 1畫完圖發(fā)現(xiàn)某條 MC 沒(méi)參與堆疊第一眼看縮進(jìn)而不是懷疑解析器。5.3 直方圖路徑?jīng)]寫對(duì)文件里明明有程序說(shuō)找不到現(xiàn)象plotIt 報(bào)找不到直方圖打開 ROOT 文件瀏覽器里一眼看到名字和配置里一模一樣。原因直方圖在文件里的真實(shí)路徑是jet1/MET配置里只寫MET或者名字旁邊帶空格。ROOT 文件的內(nèi)部目錄結(jié)構(gòu)很少顯示在文件名里瀏覽器里看到的是完整路徑但容易看漏前綴。還有一類是文件路徑帶中文目錄名部分版本 ROOT 的文件 IO 對(duì)非 ASCII 路徑支持不穩(wěn)定。解決寫配置前先打印文件里的對(duì)象路徑import ROOT f ROOT.TFile.Open(mc.root) for k in f.GetListOfKeys(): print(k.GetName(), k.GetClassName())如果返回jets/MET配置里的 histo 就寫jets/MET。文件路徑和輸出目錄盡量純 ASCII。這條花不到兩分鐘能避掉大量“配置沒(méi)問(wèn)題但就是畫不出來(lái)”的玄學(xué)。還有人把.root寫進(jìn) histo 字段這是把文件路徑和文件內(nèi)部路徑混了。5.4 重跑之后輸出不更新舊 PDF 比新代碼活得久現(xiàn)象改完 YAML 重跑 plotIt打開 PDF 和上次一模一樣再改一次還是老樣子。原因最常見三種。一是忘了更新模式參數(shù)工具為了省時(shí)跳過(guò)已有輸出二是輸出路徑變了新 PDF 寫在plots_v1打開的是plots_v2里的舊文件三是 PDF 查看器緩存頁(yè)面沒(méi)刷新。解決先ls -lt看輸出目錄的時(shí)間戳確認(rèn)剛生成的到底是哪個(gè)文件。命令行顯式重跑加更新模式。我的固定動(dòng)作是每次重跑終端打印輸出文件路徑然后直接復(fù)制路徑去打開不靠猜。ls -lt能救掉一半“怎么沒(méi)生效”。5.5 堆疊數(shù)量被重復(fù)歸一化疊出來(lái)的總形狀莫名其妙現(xiàn)象開 ratio 面板后比值整體漂在 0.5 或 1.5 附近或者 QCD 和 Wjets 各自正常疊起來(lái)總形狀對(duì)不上 Data。原因上游 ROOT 文件里的 MC 已經(jīng)乘過(guò)權(quán)重配置的歸一化又整組歸一了一次或者每個(gè) MC 都開了單獨(dú)歸一化疊完后每一份都變成單位面積堆疊總形狀自然是錯(cuò)的。解決歸一化邏輯只留一層。是“MC 總積分對(duì)齊 Data 總積分”還是“每類 MC 單獨(dú)歸一化到 1”二選一不要混。我的習(xí)慣是上游分析代碼已經(jīng)做絕對(duì)歸一化繪圖配置里不再開歸一化需要做形狀對(duì)比時(shí)才臨時(shí)開。這個(gè)坑的詭異之處在于它不報(bào)錯(cuò)只在數(shù)值關(guān)系上露餡所以每次畫完堆疊圖先看總積分再看比值面板基準(zhǔn)線是否貼 1。6. 進(jìn)階把二十張圖的迭代速度壓到一分鐘以內(nèi)配置和模板循環(huán)穩(wěn)定后整個(gè)出圖流程就變成一套可重復(fù)的流水線。最后補(bǔ)三個(gè)讓效率再高一截的小習(xí)慣。第一個(gè)是把循環(huán)腳本改成“失敗繼續(xù)”模式for f in configs/*.yml; do echo $f plotIt -u $f || echo FAILED: $f done常規(guī)腳本遇到一次失敗會(huì)中斷剩下十幾張全停。加|| echo FAILED: $f后單張失敗只打一行字整個(gè)循環(huán)跑完回頭統(tǒng)一看哪些失敗。做變量掃描時(shí)這個(gè)習(xí)慣把出圖從“等著中斷”變成“等著結(jié)果報(bào)告”。第二個(gè)是驗(yàn)證輸出不只是文件存在而是內(nèi)容可讀。批量出圖后順手pdftotext plots_v1/Data_MET.pdf - | grep MET能 grep 到標(biāo)題文本說(shuō)明 PDF 不是空白頁(yè)字體也沒(méi)崩成亂碼。配合ls -lt plots_v1/幾分鐘內(nèi)完成“所有圖都畫了、內(nèi)容都對(duì)了”的雙重確認(rèn)。這個(gè)習(xí)慣幫我抓住過(guò)一次字體雪崩——文件生成了二十個(gè)每個(gè)都是空白 PDF就是被這條命令攔下來(lái)的。第三個(gè)是把配置納入版本管理。datasets 和 layout 都是純文本 YAML跟著代碼進(jìn)倉(cāng)庫(kù)直方圖 ROOT 文件體積太大不進(jìn)。這樣每次出圖都能追溯“當(dāng)時(shí)的配置是哪一次提交”。評(píng)審問(wèn)某張圖的歸一化是誰(shuí)加的git log 一查清清楚楚。我現(xiàn)在的新變量出圖流程是在分析代碼里加一行 Fill跑完作業(yè)把 template.yml 里的 HISTONAME 改掉執(zhí)行循環(huán)腳本打開 PDF 掃一眼全套一分鐘以內(nèi)。宏文件里挪圖例、調(diào)坐標(biāo)軸的苦基本告別了。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取