W poprzedniej lekcji manipulowaliśmy histogramami. Teraz czas pokazać opcje, które również się przydają do poprawiania wyglądu histogramu, ale nie są wywoływane na samym histogramie.

Osie

Na pierwszej lekcji o histogramach pokazałem, że SetTitle może służyć do ustawienia tytuły osi, można tu jednak robić znacznie więcej, najważniejsze metody to:

  • SetNDivisions  - pozwala ustawić ile "podziałek będzie na histogramie"
  • SetLabelOffset - pozwala ustawić odległość między liczbami a osią (uwaga domyśla wartość jest bardzo mała 0.005, tak więc SetLabelOffset(1) zapewne spowoduje że liczby opisujące dane "wylecą" za rysowany obszar)
  • SetLabelSize - pozwala ustawić wielkość liczby na osi (domyśla wartość 0.035)
  • podobnie SetTitleOffset i SetTitleSize ustawia opis osi w danej odległości od osi

Metody te "można wyklikać", tzn. rysujemy sobie histogram jakimś makrem, a następnie klikamy wokół osi i otrzymujemy takie menu:

Rys. 1. Menu osi.

Ważne jest tutaj, aby upewnić się, że pierwsze co jest w rozwijanym menu jest być osią (TAxis) - podobne menu może służyć np. do manipulacji padem.

Osie również są o tyle użyteczne, że czasem nie jest znana szerokość binu, bądź też musimy znaleźć numer binu odpowiadający jakieś wartości, w tym przypadku należy użyć:

  • TH1D::GetXaxis()->GetBinCenter(i) - zwraca wartość środka binu (nie mylić z zawartością binu GetBinContent), można również używać GetBinLowEdge(i) i GetBinUpEdge(i) aby znaleźć granice przedziału dla danego binu
  • TH1D::GetXaxis()->GetBinWidth(i) - zwraca szerokość binu
  • TH1D::GetXaxis()->FindBin(x) - zwraca numer binu odpowiadający wartości x

Nieco inaczej wygląda sprawa rysowania osi bez użycia histogramu jako takiego. Tak się zdarza, gdy chcemy porównać dwa przeskalowane histogramy - nie możemy wtedy użyć opcji Draw("SAME") i skorzystać z jednej osi. Dlaczego?

Rozważmy sytuację gdzie chcemy porównać histogram mający maksymalnie 1000 wejść w binie z takim który ma ich maksymalnie milion (tzw. "większy" histogram). Teraz mamy 3 wyjścia:

  • rysujemy pierwszy histogram normalnie, drugi z opcją "SAME", pojawia się jednak problem - maksimum na OY pierwszego histogramu to 1100 (1000 x 110%), ponieważ drugi histogram będzie się musiał dostosować do pierwszego, zostanie "ucięty" - tzn. nie będziemy widzieć binów, które mają więcej niż 1100 wejść
  • możemy odwrócić kolejność rysowania histogramów, wtedy OY dostosuje się do "większego" histogramu, ewentualnie możemy dostosować ręcznie OY w pierwszym histogramie, niestety w obu przypadkach mamy histogramy różniące się znacznie ilością wejść przez co ciężko je porównać
  • najlepszą więc opcją jest przeskalowanie jednego histogramu tak aby "nakładał" się na drugi przy rysowaniu z opcją "SAME", w takim wypadku jednak potrzebujemy dodatkowej osi (do opisu przeskalowanego histogramu)

Taką sytuację przedstawia rysunek 2 oraz makro poniżej.

void histogram_1_4(){
    TCanvas *c1 = new TCanvas();
    c1->Divide(1,1);
    TRandom3 *rand = new TRandom3();
    TH1D *histogram = new TH1D("name","title",100,-2.0,2.0);
    TH1D *histogram2 = new TH1D("name2","title2",100,-2.0,2.0);
    histogram2->SetLineColor(kRed);
    for(Int_t i=0;i<1000;i++){
        histogram->Fill(rand->Gaus(0.0,1.0));
        histogram2->Fill(rand->Gaus(0.0,0.5));
    }   
    histogram->Draw();
    c1->Update();
    TPad *pad = (TPad*)c1->GetPad(0);
    Float_t rightmax = 1.1*histogram2->GetMaximum();
    Float_t scale = pad->GetUymax()/rightmax;
    histogram2->Scale(scale);
    histogram2->Draw("same");
    TGaxis *axis = new TGaxis(pad->GetUxmax(),pad->GetUymin(),
pad->GetUxmax(),pad->GetUymax(),0,rightmax,510,"+L");
    axis->SetLineColor(kBlue);
    axis->SetLabelColor(kBlue);
    axis->Draw();
}

Co więc się tutaj dzieje? Na początku tworzymy dwa histogramy i rysujemy pierwszy. Następnie pobieramy pad na którym był narysowany pierwszy histogram (linia 14). Następnie wyliczamy skalę, przez którą przemnożymy drugi histogram tak aby go porównać (linie 15-17). Dopiero po przeskalowaniu rysujemy drugi histogram. Ponieważ jednak drugi histogram był skalowany, trzeba stworzyć skalowaną oś dla niego. "Ręcznie" rysowaną oś reprezentuje klasa TGaxis. Warto tu zwrócić uwagę że TGAxis liczy współrzędne wg "rysowanych" wartości a nie wg. bezwzględnych współrzędnych (w których dolny prawy róg to 0,0 a górny lewy 1,1). "Rysowanle współrzędne" zależą od wartości przechowywanych w histogramie. W celu ich wyznaczenia używane są metody typu GetUymin czy GetUymax (które zwróci 24.15 bo na tej wartości się kończy OY). Przedostatni argument konstruktora TGaxis określa jak podzielona będzie oś (510 definiuje tzn. NDivisions) a "+L" każe rysować podziałkę osi po lewej stronie.

 

Rys. 2. Histogramy rysowane z różnymi skalami.

Pady

Pady to "podokienka" na których rysuje się histogramy. Do aktualnego padu możemy się odwołać poprzez obiekt gPad. Najważniejsze metody padów to:

  • SetLogx(), SetLogy(), SetLogZ() - ustawia skalę logarytmiczną na danej osi
  • SetGridx(), SetGridy() - rysuje siatkę
  • SetBottom/Left/Right/TopMargin - ustawiają marginesy tzn. definiują jak daleko od krawędzi padów będą krawędzie histogramu

Rys. 3. Pad z histogramem i marginesem dolnym ustawionym na zero.

Na poprzednich lekcjach pokazano, że TCanvas można podzielić na podokienka metodą Divide, zasadniczym problemem jest tutaj jednak to, że w takim wypadku nie posiadamy kontroli na rozmiarami padów. - wszystkie są takie same. W przypadku gdy chcemy narysować coś takiego:

Rys. 4. "Ręcznie tworzone pady.

należy "ręcznie" tworzyć pady:

void histo(){
	TH1D *h1 = new TH1D("a","a",20,-1,1);
	TH1D *h2 = new TH1D("b","b",20,-1,1);
	TH1D *h3 = new TH1D("c","c",20,-1,1);
	for(int i=0;i<1000;i++){
		h1->Fill(gRandom->Gaus(0,0.5));
		h2->Fill(gRandom->Gaus(0,1));
		h3->Fill(gRandom->Gaus(0,0.25));
	}

	TCanvas *c = new TCanvas();
	TPad *pad1 = new TPad("pad1", "pad1", 0, 0.5, 1, 1 );
	pad1->SetFillColor(kCyan);
	pad1->Draw();
	pad1->cd();
	h1->Draw();
	c->cd();
	TPad *pad2 = new TPad("pad2", "pad2", 0, 0, 0.5, 0.5 );
	pad2->SetFillColor(kYellow);
	pad2->Draw();
	pad2->cd();
	h2->Draw();
	c->cd();
	TPad *pad3 = new TPad("pad3", "pad3", 0.5, 0, 1, 0.5 );
	pad3->SetFillColor(kOrange);
	pad3->Draw();
	pad3->cd();
	h3->Draw();
	c->cd();
}

W konstruktorach padów podajemy współrzędne w skali "globalnej", dolny prawy róg ma współrzędne x=0 i y=0 a górny lewy x=1, y=1. Należy tu zwrócić uwagę na linijie 17,23 i 29. Służą one do powrotu do padu głównego okienka TCanvas, gdyby nie to, pady 2 i pady 3 zostałyby narysowane na pierwszym padzie (błękitnym), a nie na okienku (TCanvas).