diff --git a/README/ReleaseNotes/v610/index.md b/README/ReleaseNotes/v610/index.md index 45ca02dac8e0a85b2bd95e5d6e6cf19cd410f1a9..d766c10141eb07628f55aa9f65e6bba85f177924 100644 --- a/README/ReleaseNotes/v610/index.md +++ b/README/ReleaseNotes/v610/index.md @@ -110,6 +110,16 @@ The following interfaces have been removed, after deprecation in v6.08. - In `TGraphPainter::PaintGrapHist`: Decouple the `P` option (histogram drawn with a simple polymarker) from the `L`(Histogram drawn as a simple polyline). This improved (in some cases some extra markers were drawn) and simplify. the code. +- Candle plot improvements: + * Rearragement of TCandle-code - split into calculate and paint + * Implementation for a "raw-data candle" inside TCandle - to be used from TTreeViewer in the future + * Implementation of 1D histograms along each candle (left, right and violin) - to be used for violin-charts + * Implementation of a zero indicator line for TCandle - to be used for violin-charts + * Reimplementation if THistPainter draw option VIOLIN + * Implementations of presets and individual options for VIOLIN-charts + * Implementation of VIOLIN-charts in THStack - can be combined with CANDLE + * Update of the docs (THistPainter and THStack) + * New tutorials ## 3D Graphics Libraries - In `TMarker3DBox::PaintH3` the boxes' sizes was not correct. diff --git a/graf2d/graf/inc/TCandle.h b/graf2d/graf/inc/TCandle.h index 4eb0aca889a5e495fc6a04de5cf1772f7571af4d..4da4d5429ae54d25bcb384a3a3bdfb5b3419e677 100644 --- a/graf2d/graf/inc/TCandle.h +++ b/graf2d/graf/inc/TCandle.h @@ -28,62 +28,87 @@ #include "TH1D.h" #include "TMath.h" +const Int_t kNMAXPOINTS = 2010; // Max outliers per candle + class TCandle : public TAttLine, public TAttFill, public TAttMarker { public: //Candle Option - enum CandleOption : int { + enum CandleOption : long { kNoOption = 0, kBox = 1, - kBoxFilled = 2, + //--------------- kMedianLine = 10, kMedianNotched = 20, kMedianCircle = 30, + //--------------- kMeanLine = 100, kMeanCircle = 300, + //--------------- kWhiskerAll = 1000, kWhisker15 = 2000, + //--------------- kAnchor = 10000, + //--------------- kPointsOutliers = 100000, kPointsAll = 200000, kPointsAllScat = 300000, - kHorizontal = 1000000 // if this bit is not set it is vertical! + //--------------- + kHistoLeft = 1000000, + kHistoRight = 2000000, + kHistoViolin = 3000000, + //--------------- + kHistoZeroIndicator = 10000000, + //--------------- + kHorizontal = 1000000000 // if this bit is not set it is vertical! }; protected: - bool fIsRaw; ///< 0: for TH1 projection, 1: using raw data + bool fIsRaw; ///< 0: for TH1 projection, 1: using raw data bool fIsCalculated; TH1D * fProj; - bool fDismiss; ///< True if the candle cannot be painted - - Double_t fPosCandleAxis; ///< x-pos for a vertical candle - Double_t fCandleWidth; ///< The candle width - - Double_t fMean; ///< Position of the mean - Double_t fMedian; ///< Position of the median - Double_t fBoxUp; ///< Position of the upper box end - Double_t fBoxDown; ///< Position of the lower box end - Double_t fWhiskerUp; ///< Position of the upper whisker end - Double_t fWhiskerDown; ///< Position of the lower whisker end - - Double_t * fDatapoints; ///< position of all Datapoints within this candle - Double_t fNDatapoints; ///< Number of Datapoints within this candle - - CandleOption fOption; ///< Setting the style of the candle - int fLogX; - int fLogY; + bool fDismiss; ///< True if the candle cannot be painted + + Double_t fPosCandleAxis; ///< x-pos for a vertical candle + Double_t fCandleWidth; ///< The candle width + + Double_t fMean; ///< Position of the mean + Double_t fMedian; ///< Position of the median + Double_t fMedianErr; ///< The size of the notch + Double_t fBoxUp; ///< Position of the upper box end + Double_t fBoxDown; ///< Position of the lower box end + Double_t fWhiskerUp; ///< Position of the upper whisker end + Double_t fWhiskerDown; ///< Position of the lower whisker end + + Double_t * fDatapoints; ///< position of all Datapoints within this candle + Long64_t fNDatapoints; ///< Number of Datapoints within this candle + + Double_t fDrawPointsX[kNMAXPOINTS]; ///< x-coord for every outlier, .. + Double_t fDrawPointsY[kNMAXPOINTS]; ///< y-coord for every outlier, .. + Long64_t fNDrawPoints; ///< max number of outliers or otherpoint to be shown + + Double_t fHistoPointsX[kNMAXPOINTS]; ///< x-coord for the polyline of the histo + Double_t fHistoPointsY[kNMAXPOINTS]; ///< y-coord for the polyline of the histo + int fNHistoPoints; + + CandleOption fOption; ///< Setting the style of the candle + int fLogX; ///< make the candle appear logx-like + int fLogY; ///< make the candle appear logy-like + + Double_t fAxisMin; ///< The Minimum which is visible by the axis (used by zero indicator) + Double_t fAxisMax; ///< The Maximum which is visible by the axis (used by zero indicator) void Calculate(); int GetCandleOption(const int pos) {return (fOption/(int)TMath::Power(10,pos))%10;} bool IsOption(CandleOption opt); - void PaintBox(Int_t nPoints, Double_t *x, Double_t *y, Bool_t swapXY, Bool_t fill); + void PaintBox(Int_t nPoints, Double_t *x, Double_t *y, Bool_t swapXY); void PaintLine(Double_t x1, Double_t y1, Double_t x2, Double_t y2, Bool_t swapXY); public: TCandle(); - TCandle(const Double_t candlePos, const Double_t candleWidth, const Int_t n, const Double_t * points); + TCandle(const Double_t candlePos, const Double_t candleWidth, Long64_t n, Double_t * points); TCandle(const Double_t candlePos, const Double_t candleWidth, TH1D *proj); TCandle(const TCandle &candle); virtual ~TCandle(); @@ -104,6 +129,7 @@ public: void SetHistogram(TH1D *proj) { fProj = proj; fIsCalculated = false;} virtual void Paint(Option_t *option=""); + void ConvertToPadCoords(Double_t minAxis, Double_t maxAxis, Double_t axisMinCoord, Double_t axisMaxCoord, Double_t minInit, Double_t maxInit); virtual void SetMean(Double_t mean) { fMean = mean; } virtual void SetMedian(Double_t median) { fMedian = median; } diff --git a/graf2d/graf/src/TCandle.cxx b/graf2d/graf/src/TCandle.cxx index b7b6ca9d9f7c39e48e6a7b0a24228e3c40d912db..4db8f84911c86d80cd5089bedf8aa588fcb69647 100644 --- a/graf2d/graf/src/TCandle.cxx +++ b/graf2d/graf/src/TCandle.cxx @@ -30,8 +30,6 @@ TCandle is the "painter class" of the box plots. Therefore it is never used directly to draw a candle. */ -const Int_t kNMAX = 2000; // Max outliers per candle - //////////////////////////////////////////////////////////////////////////////// /// TCandle default constructor. @@ -43,17 +41,47 @@ TCandle::TCandle() fCandleWidth = 1.0; fMean = 0.; fMedian = 0.; + fMedianErr = 0; fBoxUp = 0.; fBoxDown = 0.; fWhiskerUp = 0.; fWhiskerDown = 0.; + fNDatapoints = 0; + fDismiss = 0; fLogX = 0; fLogY = 0; + fNDrawPoints = 0; + fNHistoPoints = 0; +} + +//////////////////////////////////////////////////////////////////////////////// +/// TCandle constructor for raw-data candles. + +TCandle::TCandle(const Double_t candlePos, const Double_t candleWidth, Long64_t n, Double_t * points) + : TAttLine(), TAttFill(), TAttMarker() +{ + //Preliminary values only, need to be calculated before paint + fMean = 0; + fMedian = 0; + fMedianErr = 0; + fBoxUp = 0; + fBoxDown = 0; + fWhiskerUp = 0; + fWhiskerDown = 0; + fNDatapoints = n; + fIsCalculated = 0; + fIsRaw = true; + fPosCandleAxis = candlePos; + fCandleWidth = candleWidth; + fDatapoints = points; + fProj = NULL; fDismiss = 0; - fProj = 0; fOption = kNoOption; - fNDatapoints = 0; - fDatapoints = 0; + fLogX = 0; + fLogY = 0; + fNDrawPoints = 0; + fNHistoPoints = 0; + } //////////////////////////////////////////////////////////////////////////////// @@ -65,6 +93,7 @@ TCandle::TCandle(const Double_t candlePos, const Double_t candleWidth, TH1D *pro //Preliminary values only, need to be calculated before paint fMean = 0; fMedian = 0; + fMedianErr = 0; fBoxUp = 0; fBoxDown = 0; fWhiskerUp = 0; @@ -80,12 +109,16 @@ TCandle::TCandle(const Double_t candlePos, const Double_t candleWidth, TH1D *pro fOption = kNoOption; fLogX = 0; fLogY = 0; + fNDrawPoints = 0; + fNHistoPoints = 0; + } //////////////////////////////////////////////////////////////////////////////// /// TCandle default destructor. TCandle::~TCandle() { + if (fIsRaw && fProj) delete fProj; } //////////////////////////////////////////////////////////////////////////////// @@ -112,16 +145,18 @@ int TCandle::ParseOption(char * opt) { if (direction == 'Y' || direction == 'H') { fOption = (CandleOption)(fOption + kHorizontal); } if (preset == '1') //Standard candle using old candle-definition fOption = (CandleOption)(fOption + fallbackCandle); - if (preset == '2') //New standard candle with better whisker definition + outlier + else if (preset == '2') //New standard candle with better whisker definition + outlier fOption = (CandleOption)(fOption + kBox + kMeanLine + kMedianLine + kWhisker15 + kAnchor + kPointsOutliers); - if (preset == '3') //Like candle2 but with a fMean as a circle + else if (preset == '3') //Like candle2 but with a fMean as a circle fOption = (CandleOption)(fOption + kBox + kMeanCircle + kMedianLine + kWhisker15 + kAnchor + kPointsOutliers); - if (preset == '4') //Like candle3 but showing the uncertainty of the fMedian as well + else if (preset == '4') //Like candle3 but showing the uncertainty of the fMedian as well fOption = (CandleOption)(fOption + kBox + kMeanCircle + kMedianNotched + kWhisker15 + kAnchor + kPointsOutliers); - if (preset == '5') //Like candle2 but showing all datapoints + else if (preset == '5') //Like candle2 but showing all datapoints fOption = (CandleOption)(fOption + kBox + kMeanLine + kMedianLine + kWhisker15 + kAnchor + kPointsAll); - if (preset == '6') //Like candle2 but showing all datapoints scattered + else if (preset == '6') //Like candle2 but showing all datapoints scattered fOption = (CandleOption)(fOption + kBox + kMeanCircle + kMedianLine + kWhisker15 + kAnchor + kPointsAllScat); + else if (preset != ' ') //For all other presets not implemented yet used fallback candle + fOption = (CandleOption)(fOption + fallbackCandle); if (preset != ' ' && direction != ' ') strncpy(l," ",8); @@ -138,8 +173,10 @@ int TCandle::ParseOption(char * opt) { char indivOption[32]; if (brOpen && brClose) { useIndivOption = true; + bool isHorizontal = IsHorizontal(); strncpy(indivOption, brOpen, brClose-brOpen +1); //Now the string "(....)" including brackets is in this array sscanf(indivOption,"(%d)", (int*) &fOption); + if (isHorizontal) {fOption = (CandleOption)(fOption + kHorizontal);} strncpy(brOpen," ",brClose-brOpen+1); //Cleanup } @@ -149,6 +186,57 @@ int TCandle::ParseOption(char * opt) { fOption = fallbackCandle; } } + + l = strstr(opt,"VIOLIN"); + if (l) { + const CandleOption fallbackCandle = (CandleOption)(kMeanCircle + kWhiskerAll + kHistoViolin + kHistoZeroIndicator); + + char direction = ' '; + char preset = ' '; + + if (l[6] >= 'A' && l[6] <= 'Z') direction = l[6]; + if (l[6] >= '1' && l[6] <= '9') preset = l[6]; + if (l[7] >= 'A' && l[7] <= 'Z' && preset != ' ') direction = l[7]; + if (l[7] >= '1' && l[7] <= '9' && direction != ' ') preset = l[7]; + + if (direction == 'X' || direction == 'V') { /* nothing */ } + if (direction == 'Y' || direction == 'H') { fOption = (CandleOption)(fOption + kHorizontal); } + if (preset == '1') //Standard candle using old candle-definition + fOption = (CandleOption)(fOption + fallbackCandle); + else if (preset == '2') //New standard candle with better whisker definition + outlier + fOption = (CandleOption)(fOption + kMeanCircle + kWhisker15 + kHistoViolin + kHistoZeroIndicator + kPointsOutliers); + else if (preset != ' ') //For all other presets not implemented yet used fallback candle + fOption = (CandleOption)(fOption + fallbackCandle); + + if (preset != ' ' && direction != ' ') + strncpy(l," ",8); + else if (preset != ' ' || direction != ' ') + strncpy(l," ",7); + else + strncpy(l," ",6); + + Bool_t useIndivOption = false; + + if (preset == ' ') { // Check if the user wants to set the properties individually + char *brOpen = strstr(opt,"("); + char *brClose = strstr(opt,")"); + char indivOption[32]; + if (brOpen && brClose) { + useIndivOption = true; + bool isHorizontal = IsHorizontal(); + strncpy(indivOption, brOpen, brClose-brOpen +1); //Now the string "(....)" including brackets is in this array + sscanf(indivOption,"(%d)", (int*) &fOption); + if (isHorizontal) {fOption = (CandleOption)(fOption + kHorizontal);} + strncpy(brOpen," ",brClose-brOpen+1); //Cleanup + + } + } + //Handle option "VIOLIN" ,"VIOLINX" or "VIOLINY" to behave like "VIOLINX1" or "VIOLINY1" + if (!useIndivOption && !fOption ) { + fOption = fallbackCandle; + } + } + fIsCalculated = false; return fOption; @@ -159,31 +247,53 @@ int TCandle::ParseOption(char * opt) { /// candle options. void TCandle::Calculate() { - if (!fIsRaw && fProj) { - // Determining the quantiles - Double_t *prob = new Double_t[5]; - prob[0]=1E-15; prob[1]=0.25; prob[2]=0.5; prob[3]=0.75; prob[4]=1-1E-15; - Double_t *quantiles = new Double_t[5]; - quantiles[0]=0.; quantiles[1]=0.; quantiles[2] = 0.; quantiles[3] = 0.; quantiles[4] = 0.; + //Reset everything + fNDrawPoints = 0; + fNHistoPoints = 0; - fProj->GetQuantiles(5, quantiles, prob); + Bool_t swapXY = IsOption(kHorizontal); + Bool_t doLogY = (!(swapXY) && fLogY) || (swapXY && fLogX); + Bool_t doLogX = (!(swapXY) && fLogX) || (swapXY && fLogY); - // Check if the quantiles are valid, seems the under- and overflow is taken - // into account as well, we need to ignore this! - if (quantiles[0] >= quantiles[4]) return; - if (quantiles[1] >= quantiles[3]) return; - - // Definition of the candle in the standard case - fBoxUp = quantiles[3]; - fBoxDown = quantiles[1]; - fWhiskerUp = quantiles[4]; //Standard case - fWhiskerDown = quantiles[0]; //Standard case - fMedian = quantiles[2]; - fMean = fProj->GetMean(); + //Will be min and max values of raw-data + Double_t min = 1e15; + Double_t max = -1e15; + + // Determining the quantiles + Double_t *prob = new Double_t[5]; + prob[0]=1E-15; prob[1]=0.25; prob[2]=0.5; prob[3]=0.75; prob[4]=1-1E-15; + Double_t *quantiles = new Double_t[5]; + quantiles[0]=0.; quantiles[1]=0.; quantiles[2] = 0.; quantiles[3] = 0.; quantiles[4] = 0.; + if (!fIsRaw && fProj) { //Need a calculation for a projected histo + if (((IsOption(kHistoLeft)) || (IsOption(kHistoRight)) || (IsOption(kHistoViolin))) && fProj->GetNbinsX() > 500) { + //When using the histooption the number of bins of the projection is + //limited because of the arrayspace defined by kNMAXPOINTS. + //So the histo is rebinned, that it can be displayed at any time. + // Finer granularity is not usefull anyhow + int divideBy = ((fProj->GetNbinsX() - 1)/((kNMAXPOINTS-10)/4))+1; + fProj->RebinX(divideBy); + } + fProj->GetQuantiles(5, quantiles, prob); + } else { //Need a calculation for a raw-data candle + TMath::Quantiles(fNDatapoints,5,fDatapoints,quantiles,prob,kFALSE); + } - Double_t iqr = fBoxUp-fBoxDown; + // Check if the quantiles are valid, seems the under- and overflow is taken + // into account as well, we need to ignore this! + if (quantiles[0] >= quantiles[4]) return; + if (quantiles[1] >= quantiles[3]) return; + + // Definition of the candle in the standard case + fBoxUp = quantiles[3]; + fBoxDown = quantiles[1]; + fWhiskerUp = quantiles[4]; //Standard case + fWhiskerDown = quantiles[0]; //Standard case + fMedian = quantiles[2]; + Double_t iqr = fBoxUp-fBoxDown; + Int_t nOutliers = 0; - if (IsOption(kWhisker15)) { // Improved whisker definition, with 1.5*iqr + if (IsOption(kWhisker15)) { // Improved whisker definition, with 1.5*iqr + if (!fIsRaw && fProj) { //Need a calculation for a projected histo int bin = fProj->FindBin(fBoxDown-1.5*iqr); // extending only to the lowest data value within this range while (fProj->GetBinContent(bin) == 0 && bin <= fProj->GetNbinsX()) bin++; @@ -192,12 +302,207 @@ void TCandle::Calculate() { bin = fProj->FindBin(fBoxUp+1.5*iqr); while (fProj->GetBinContent(bin) == 0 && bin >= 1) bin--; fWhiskerUp = fProj->GetBinCenter(bin); + } else { //Need a calculation for a raw-data candle + fWhiskerUp = fBoxDown; + fWhiskerDown = fBoxUp; + + //Need to find highest value up to 1.5*iqr from the BoxUp-pos, and the lowest value up to -1.5*iqr from the boxLow-pos + for (Long64_t i = 0; i < fNDatapoints; ++i) { + Double_t myData = fDatapoints[i]; + if (myData > fWhiskerUp && myData <= fBoxUp + 1.5*iqr) fWhiskerUp = myData; + if (myData < fWhiskerDown && myData >= fBoxDown - 1.5*iqr) fWhiskerDown = myData; + } } + } - delete [] prob; - delete [] quantiles; - } else if (fIsRaw) { + if (!fIsRaw && fProj) { //Need a calculation for a projected histo + fMean = fProj->GetMean(); + fMedianErr = 1.57*iqr/sqrt(fProj->GetEntries()); + fAxisMin = fProj->GetXaxis()->GetXmin(); + fAxisMax = fProj->GetXaxis()->GetXmax(); + } else { //Need a calculation for a raw-data candle + //Calculate the Mean + fMean = 0; + for (Long64_t i = 0; i < fNDatapoints; ++i) { + fMean += fDatapoints[i]; + if (fDatapoints[i] < min) min = fDatapoints[i]; + if (fDatapoints[i] > max) max = fDatapoints[i]; + if (fDatapoints[i] < fWhiskerDown || fDatapoints[i] > fWhiskerUp) nOutliers++; + } + fMean /= fNDatapoints; + fMedianErr = 1.57*iqr/sqrt(fNDatapoints); } + + delete [] prob; + delete [] quantiles; + + //Doing the outliers and other single points to show + if (GetCandleOption(5) > 0) { //Draw outliers + TRandom2 random; + const int maxOutliers = kNMAXPOINTS; + Double_t myScale = 1.; + if (!fIsRaw && fProj) { //Need a calculation for a projected histo + if (fProj->GetEntries() > maxOutliers/2) myScale = fProj->GetEntries()/(maxOutliers/2.); + fNDrawPoints = 0; + for (int bin = 0; bin < fProj->GetNbinsX(); bin++) { + // Either show them only outside the whiskers, or all of them + if (fProj->GetBinContent(bin) > 0 && (fProj->GetBinCenter(bin) < fWhiskerDown || fProj->GetBinCenter(bin) > fWhiskerUp || (GetCandleOption(5) > 1)) ) { + Double_t scaledBinContent = fProj->GetBinContent(bin)/myScale; + if (scaledBinContent >0 && scaledBinContent < 1) scaledBinContent = 1; //Outliers have a typical bincontent between 0 and 1, when scaling they would disappear + for (int j=0; j < (int)scaledBinContent; j++) { + if (fNDrawPoints > maxOutliers) break; + if (IsOption(kPointsAllScat)) { //Draw outliers and "all" values scattered + fDrawPointsX[fNDrawPoints] = fPosCandleAxis - fCandleWidth/2. + fCandleWidth*random.Rndm(); + fDrawPointsY[fNDrawPoints] = fProj->GetBinLowEdge(bin) + fProj->GetBinWidth(bin)*random.Rndm(); + } else { //Draw them in the "candle line" + fDrawPointsX[fNDrawPoints] = fPosCandleAxis; + if ((int)scaledBinContent == 1) //If there is only one datapoint available put it in the middle of the bin + fDrawPointsY[fNDrawPoints] = fProj->GetBinCenter(bin); + else //If there is more than one datapoint scatter it along the bin, otherwise all marker would be (invisibly) stacked on top of each other + fDrawPointsY[fNDrawPoints] = fProj->GetBinLowEdge(bin) + fProj->GetBinWidth(bin)*random.Rndm(); + } + if (swapXY) { + //Swap X and Y + Double_t keepCurrently; + keepCurrently = fDrawPointsX[fNDrawPoints]; + fDrawPointsX[fNDrawPoints] = fDrawPointsY[fNDrawPoints]; + fDrawPointsY[fNDrawPoints] = keepCurrently; + } + // Continue fMeans, that fNDrawPoints is not increased, so that value will not be shown + if (doLogX) { + if (fDrawPointsX[fNDrawPoints] > 0) fDrawPointsX[fNDrawPoints] = TMath::Log10(fDrawPointsX[fNDrawPoints]); else continue; + } + if (doLogY) { + if (fDrawPointsY[fNDrawPoints] > 0) fDrawPointsY[fNDrawPoints] = TMath::Log10(fDrawPointsY[fNDrawPoints]); else continue; + } + fNDrawPoints++; + } + } + if (fNDrawPoints > maxOutliers) { //Should never happen, due to myScale!!! + Error ("PaintCandlePlot","Not possible to draw all outliers."); + break; + } + } + } else { //Raw data candle + //If only outliers are shown, calculate myScale only based on nOutliers, use fNDatapoints (all) instead + if (IsOption(kPointsOutliers) && nOutliers > maxOutliers/2) { + myScale = nOutliers/(maxOutliers/2.); + } else { + if (fNDatapoints > maxOutliers/2) myScale = fNDatapoints/(maxOutliers/2.); + } + fNDrawPoints = 0; + for (int i = 0; i < fNDatapoints; i++ ) { + Double_t myData = fDatapoints[i]; + Double_t maxScatter = (fWhiskerUp-fWhiskerDown)/100; + if (!(i % (int) myScale == 0 )) continue; //If the amount of data is too large take only every 2nd or 3rd to reduce the amount + // Either show them only outside the whiskers, or all of them + if (myData < fWhiskerDown || myData > fWhiskerUp || (GetCandleOption(5) > 1)) { + if (IsOption(kPointsAllScat)) { //Draw outliers and "all" values scattered + fDrawPointsX[fNDrawPoints] = fPosCandleAxis - fCandleWidth/2. + fCandleWidth*random.Rndm(); + fDrawPointsY[fNDrawPoints] = myData + (random.Rndm() - 0.5)*maxScatter; //random +- 0.5 of candleheight + } else { //Draw them in the "candle line" + fDrawPointsX[fNDrawPoints] = fPosCandleAxis; + fDrawPointsY[fNDrawPoints] = myData + (random.Rndm() - 0.5)*maxScatter; //random +- 0.5 of candleheight + } + if (swapXY) { + //Swap X and Y + Double_t keepCurrently; + keepCurrently = fDrawPointsX[fNDrawPoints]; + fDrawPointsX[fNDrawPoints] = fDrawPointsY[fNDrawPoints]; + fDrawPointsY[fNDrawPoints] = keepCurrently; + } + // Continue fMeans, that fNDrawPoints is not increased, so that value will not be shown + if (doLogX) { + if (fDrawPointsX[fNDrawPoints] > 0) fDrawPointsX[fNDrawPoints] = TMath::Log10(fDrawPointsX[fNDrawPoints]); + else continue; + } + if (doLogY) { + if (fDrawPointsY[fNDrawPoints] > 0) fDrawPointsY[fNDrawPoints] = TMath::Log10(fDrawPointsY[fNDrawPoints]); + else continue; + } + fNDrawPoints++; + if (fNDrawPoints > maxOutliers) { //Should never happen, due to myScale!!! + Error ("PaintCandlePlotRaw","Not possible to draw all outliers."); + break; + } + } + } + } + } + + if (IsOption(kHistoRight) || IsOption(kHistoLeft) || IsOption(kHistoViolin)) { + //We are starting with kHistoRight, left will be modified from right later + if (fIsRaw) { //This is a raw-data candle + if (!fProj) { + fProj = new TH1D("hpa","hpa",100,min,max+0.0001*(max-min)); + for (Long64_t i = 0; i < fNDatapoints; ++i) { + fProj->Fill(fDatapoints[i]); + } + } + } + + fNHistoPoints = 0; + Double_t maxContent = fProj->GetMaximum(); + Double_t maxHistoHeight = fCandleWidth*0.8; + if (IsOption(kHistoViolin)) maxHistoHeight *= 0.5; + + bool isFirst = true; + int lastNonZero = 0; + for (int bin = 1; bin <= fProj->GetNbinsX(); bin++) { + if (isFirst) { + if (fProj->GetBinContent(bin) > 0) { + fHistoPointsX[fNHistoPoints] = fPosCandleAxis; + fHistoPointsY[fNHistoPoints] = fProj->GetBinLowEdge(bin); + if (doLogX) { + if (fHistoPointsX[fNHistoPoints] > 0) fHistoPointsX[fNHistoPoints] = TMath::Log10(fHistoPointsX[fNHistoPoints]); else continue; + } + if (doLogY) { + if (fHistoPointsY[fNHistoPoints] > 0) fHistoPointsY[fNHistoPoints] = TMath::Log10(fHistoPointsY[fNHistoPoints]); else continue; + } + fNHistoPoints++; + isFirst = false; + } else { + continue; + } + } + Double_t myBinValue = fProj->GetBinContent(bin); + fHistoPointsX[fNHistoPoints] = fPosCandleAxis + myBinValue/maxContent*maxHistoHeight; + fHistoPointsY[fNHistoPoints] = fProj->GetBinLowEdge(bin); + fNHistoPoints++; + fHistoPointsX[fNHistoPoints] = fPosCandleAxis + myBinValue/maxContent*maxHistoHeight; + fHistoPointsY[fNHistoPoints] = fProj->GetBinLowEdge(bin)+fProj->GetBinWidth(bin); + if (doLogX) { + if (fHistoPointsX[fNHistoPoints -1] > 0) fHistoPointsX[fNHistoPoints - 1] = TMath::Log10(fHistoPointsX[fNHistoPoints - 1]); else continue; + if (fHistoPointsX[fNHistoPoints] > 0) fHistoPointsX[fNHistoPoints] = TMath::Log10(fHistoPointsX[fNHistoPoints]); else continue; + } + if (doLogY) { + if (fHistoPointsY[fNHistoPoints -1] > 0) fHistoPointsY[fNHistoPoints - 1] = TMath::Log10(fHistoPointsY[fNHistoPoints - 1]); else continue; + if (fHistoPointsY[fNHistoPoints] > 0) fHistoPointsY[fNHistoPoints] = TMath::Log10(fHistoPointsY[fNHistoPoints]); else continue; + } + + fNHistoPoints++; + if (fProj->GetBinContent(bin) > 0) lastNonZero = fNHistoPoints; + } + + fHistoPointsX[fNHistoPoints] = fPosCandleAxis; + fHistoPointsY[fNHistoPoints] = fHistoPointsY[fNHistoPoints-1]; + fNHistoPoints = lastNonZero+1; //+1 so that the line down to 0 is added as well + + if (IsOption(kHistoLeft)) { + for (int i = 0; i < fNHistoPoints; i++) { + fHistoPointsX[i] = 2*fPosCandleAxis - fHistoPointsX[i]; + } + } + if (IsOption(kHistoViolin)) { + for (int i = 0; i < fNHistoPoints; i++) { + fHistoPointsX[fNHistoPoints + i] = 2*fPosCandleAxis - fHistoPointsX[fNHistoPoints -i-1]; + fHistoPointsY[fNHistoPoints + i] = fHistoPointsY[fNHistoPoints -i-1]; + } + fNHistoPoints *= 2; + } + } + + fIsCalculated = true; } @@ -212,11 +517,12 @@ void TCandle::Paint(Option_t *) // Save the attributes as they were set originally Style_t saveLine = GetLineStyle(); Style_t saveMarker = GetMarkerStyle(); + Style_t saveFillStyle = GetFillStyle(); + Style_t saveFillColor = GetFillColor(); + Style_t saveLineColor = GetLineColor(); Double_t dimLeft = fPosCandleAxis-0.5*fCandleWidth; Double_t dimRight = fPosCandleAxis+0.5*fCandleWidth; - Double_t iqr = fBoxUp-fBoxDown; - Double_t fMedianErr = 1.57*iqr/sqrt(fProj->GetEntries()); TAttLine::Modify(); TAttFill::Modify(); @@ -228,29 +534,44 @@ void TCandle::Paint(Option_t *) // From now on this is real painting only, no calculations anymore + if (IsOption(kHistoZeroIndicator)) { + SetLineColor(saveFillColor); + TAttLine::Modify(); + PaintLine(fPosCandleAxis, fAxisMin, fPosCandleAxis, fAxisMax, swapXY); + SetLineColor(saveLineColor); + TAttLine::Modify(); + } + + + if (IsOption(kHistoRight) || IsOption(kHistoLeft) || IsOption(kHistoViolin)) { + if (IsOption(kHistoZeroIndicator) && (saveFillStyle != 0)) { + SetLineColor(saveFillColor); + TAttLine::Modify(); + } + if (!swapXY) { + gPad->PaintFillArea(fNHistoPoints, fHistoPointsX, fHistoPointsY); + gPad->PaintPolyLine(fNHistoPoints, fHistoPointsX, fHistoPointsY); + } else { + gPad->PaintFillArea(fNHistoPoints, fHistoPointsY, fHistoPointsX); + gPad->PaintPolyLine(fNHistoPoints, fHistoPointsY, fHistoPointsX); + } + if (IsOption(kHistoZeroIndicator) && (saveFillStyle != 0)) { + SetLineColor(saveLineColor); + TAttLine::Modify(); + } + } + if (IsOption(kBox)) { // Draw a simple box if (IsOption(kMedianNotched)) { // Check if we have to draw a box with notches Double_t x[] = {dimLeft, dimLeft, dimLeft+fCandleWidth/3., dimLeft, dimLeft, dimRight, dimRight, dimRight-fCandleWidth/3., dimRight, dimRight, dimLeft}; Double_t y[] = {fBoxDown, fMedian-fMedianErr, fMedian, fMedian+fMedianErr, fBoxUp, fBoxUp, fMedian+fMedianErr, fMedian, fMedian-fMedianErr, fBoxDown, fBoxDown}; - PaintBox(11, x, y, swapXY, kFALSE); - } else { // draw a simple box - Double_t x[] = {dimLeft, dimLeft, dimRight, dimRight, dimLeft}; - Double_t y[] = {fBoxDown, fBoxUp, fBoxUp, fBoxDown, fBoxDown}; - PaintBox(5, x, y, swapXY, kFALSE); - } - } else if (IsOption(kBoxFilled)) { // Draw a filled box - if (IsOption(kMedianNotched)) { // Check if we have to draw a box with notches - Double_t x[] = {dimLeft, dimLeft, dimLeft+fCandleWidth/3., dimLeft, dimLeft, dimRight, - dimRight, dimRight-fCandleWidth/3., dimRight, dimRight, dimLeft}; - Double_t y[] = {fBoxDown, fMedian-fMedianErr, fMedian, fMedian+fMedianErr, fBoxUp, fBoxUp, - fMedian+fMedianErr, fMedian, fMedian-fMedianErr, fBoxDown, fBoxDown}; - PaintBox(11, x, y, swapXY, kTRUE); + PaintBox(11, x, y, swapXY); } else { // draw a simple box Double_t x[] = {dimLeft, dimLeft, dimRight, dimRight, dimLeft}; Double_t y[] = {fBoxDown, fBoxUp, fBoxUp, fBoxDown, fBoxDown}; - PaintBox(5, x, y, swapXY, kTRUE); + PaintBox(5, x, y, swapXY); } } @@ -259,14 +580,14 @@ void TCandle::Paint(Option_t *) PaintLine(dimLeft, fWhiskerDown, dimRight, fWhiskerDown, swapXY); } - if (IsOption(kWhiskerAll)) { // Whiskers are dashed + if (IsOption(kWhiskerAll) && !IsOption(kHistoZeroIndicator)) { // Whiskers are dashed SetLineStyle(2); TAttLine::Modify(); PaintLine(fPosCandleAxis, fWhiskerUp, fPosCandleAxis, fBoxUp, swapXY); PaintLine(fPosCandleAxis, fBoxDown, fPosCandleAxis, fWhiskerDown, swapXY); SetLineStyle(saveLine); TAttLine::Modify(); - } else if (IsOption(kWhisker15)) { // Whiskers without dashing, better whisker definition (done above) + } else if ((IsOption(kWhiskerAll) && IsOption(kHistoZeroIndicator)) || IsOption(kWhisker15) ) { // Whiskers without dashing, better whisker definition, or forced when using zero line PaintLine(fPosCandleAxis, fWhiskerUp, fPosCandleAxis, fBoxUp, swapXY); PaintLine(fPosCandleAxis, fBoxDown, fPosCandleAxis, fWhiskerDown, swapXY); } @@ -303,7 +624,7 @@ void TCandle::Paint(Option_t *) } - if (IsOption(kMeanCircle)) { // Paint fMean as a circle + if (IsOption(kMeanCircle)) { // Paint fMean as a circle Double_t myMeanX[1], myMeanY[1]; if (!swapXY) { myMeanX[0] = fPosCandleAxis; @@ -348,61 +669,15 @@ void TCandle::Paint(Option_t *) // only the datapoints outside the whiskers are shown. // One can show them in one row as crosses, or scattered randomly. If activated // all datapoint are shown in the same way - TRandom2 random; - if (GetCandleOption(5) > 0) { //Draw outliers - const int maxOutliers = kNMAX; - Double_t outliersX[maxOutliers]; - Double_t outliersY[maxOutliers]; - Double_t myScale = 1.; - if (fProj->GetEntries() > maxOutliers/2) myScale = fProj->GetEntries()/(maxOutliers/2.); - int nOutliers = 0; - for (int bin = 0; bin < fProj->GetNbinsX(); bin++) { - // Either show them only outside the whiskers, or all of them - if (fProj->GetBinContent(bin) > 0 && (fProj->GetBinCenter(bin) < fWhiskerDown || fProj->GetBinCenter(bin) > fWhiskerUp || (GetCandleOption(5) > 1)) ) { - Double_t scaledBinContent = fProj->GetBinContent(bin)/myScale; - if (scaledBinContent >0 && scaledBinContent < 1) scaledBinContent = 1; //Outliers have a typical bincontent between 0 and 1, when scaling they would disappear - for (int j=0; j < (int)scaledBinContent; j++) { - if (nOutliers >= maxOutliers) break; - if (IsOption(kPointsAllScat)) { //Draw outliers and "all" values scattered - outliersX[nOutliers] = fPosCandleAxis - fCandleWidth/2. + fCandleWidth*random.Rndm(); - outliersY[nOutliers] = fProj->GetBinLowEdge(bin) + fProj->GetBinWidth(bin)*random.Rndm(); - } else { //Draw them in the "candle line" - outliersX[nOutliers] = fPosCandleAxis; - if ((int)scaledBinContent == 1) //If there is only one datapoint available put it in the middle of the bin - outliersY[nOutliers] = fProj->GetBinCenter(bin); - else //If there is more than one datapoint scatter it along the bin, otherwise all marker would be (invisibly) stacked on top of each other - outliersY[nOutliers] = fProj->GetBinLowEdge(bin) + fProj->GetBinWidth(bin)*random.Rndm(); - } - if (swapXY) { - //Swap X and Y - Double_t keepCurrently; - keepCurrently = outliersX[nOutliers]; - outliersX[nOutliers] = outliersY[nOutliers]; - outliersY[nOutliers] = keepCurrently; - } - // Continue fMeans, that nOutliers is not increased, so that value will not be shown - if (doLogX) { - if (outliersX[nOutliers] > 0) outliersX[nOutliers] = TMath::Log10(outliersX[nOutliers]); else continue; - } - if (doLogY) { - if (outliersY[nOutliers] > 0) outliersY[nOutliers] = TMath::Log10(outliersY[nOutliers]); else continue; - } - nOutliers++; - } - } - if (nOutliers > maxOutliers) { //Should never happen, due to myScale!!! - Error ("PaintCandlePlot","Not possible to draw all outliers."); - break; - } - } - if (IsOption(kPointsAllScat)) { //Draw outliers and "all" values scattered + if (GetCandleOption(5) > 0) { //Draw outliers + if (IsOption(kPointsAllScat)) { //Draw outliers and "all" values scattered SetMarkerStyle(0); } else { SetMarkerStyle(5); } TAttMarker::Modify(); - gPad->PaintPolyMarker(nOutliers,outliersX, outliersY); + gPad->PaintPolyMarker(fNDrawPoints,fDrawPointsX, fDrawPointsY); } } @@ -410,9 +685,9 @@ void TCandle::Paint(Option_t *) /// Return true is this option is activated in fOption bool TCandle::IsOption(CandleOption opt) { - int myOpt = 9; + long myOpt = 9; int pos = 0; - for (pos = 0; pos < 7; pos++) { + for (pos = 0; pos < 16; pos++) { if (myOpt > opt) break; else myOpt *=10; } @@ -425,7 +700,7 @@ bool TCandle::IsOption(CandleOption opt) { //////////////////////////////////////////////////////////////////////////////// /// Paint a box for candle. -void TCandle::PaintBox(Int_t nPoints, Double_t *x, Double_t *y, Bool_t swapXY, Bool_t fill) +void TCandle::PaintBox(Int_t nPoints, Double_t *x, Double_t *y, Bool_t swapXY) { Bool_t doLogY = (!(swapXY) && fLogY) || (swapXY && fLogX); Bool_t doLogX = (!(swapXY) && fLogX) || (swapXY && fLogY); @@ -442,11 +717,10 @@ void TCandle::PaintBox(Int_t nPoints, Double_t *x, Double_t *y, Bool_t swapXY, B } } if (!swapXY) { - if (fill) gPad->PaintFillArea(nPoints, x, y); - + gPad->PaintFillArea(nPoints, x, y); gPad->PaintPolyLine(nPoints, x, y); } else { - if (fill) gPad->PaintFillArea(nPoints, y, x); + gPad->PaintFillArea(nPoints, y, x); gPad->PaintPolyLine(nPoints, y, x); } } @@ -489,3 +763,39 @@ void TCandle::Streamer(TBuffer &R__b) R__b.WriteClassBuffer(TCandle::Class(),this); } } + +//////////////////////////////////////////////////////////////////////////////// +/// The coordinates in the TParallelCoordVar-class are in Pad-Coordinates, so we need to convert them + +void TCandle::ConvertToPadCoords(Double_t minAxis, Double_t maxAxis, Double_t axisMinCoord, Double_t axisMaxCoord, Double_t fMinInit, Double_t fMaxInit) +{ + if (!fIsCalculated) Calculate(); + Double_t a,b,maxinit,mininit; + if (fLogY) { + a = TMath::Log10(minAxis); + b = TMath::Log10(maxAxis/minAxis); + if(fMinInit > 0) mininit = TMath::Log10(fMinInit); + else mininit = TMath::Log10(axisMinCoord); + maxinit = TMath::Log10(fMaxInit); + } else { + a = minAxis; + b = maxAxis-minAxis; + mininit = fMinInit; + maxinit = fMaxInit; + } + + fMean = axisMinCoord + ((fMean-a)/b)*(axisMaxCoord-axisMinCoord); + fMedian = axisMinCoord + ((fMedian-a)/b)*(axisMaxCoord-axisMinCoord); + fMedianErr = axisMinCoord + ((fMedianErr-a)/b)*(axisMaxCoord-axisMinCoord); + fBoxUp = axisMinCoord + ((fBoxUp-a)/b)*(axisMaxCoord-axisMinCoord); + fBoxDown = axisMinCoord + ((fBoxDown-a)/b)*(axisMaxCoord-axisMinCoord); + fWhiskerUp = axisMinCoord + ((fWhiskerUp-a)/b)*(axisMaxCoord-axisMinCoord); + fWhiskerDown = axisMinCoord + ((fWhiskerDown-a)/b)*(axisMaxCoord-axisMinCoord); + + for (int i = 0; i < fNDrawPoints; i++) { + fDrawPointsY[i] = axisMinCoord + ((fDrawPointsY[i]-a)/b)*(axisMaxCoord-axisMinCoord); + } + for (int i = 0; i < fNHistoPoints; i++) { + fHistoPointsY[i] = axisMinCoord + ((fHistoPointsY[i]-a)/b)*(axisMaxCoord-axisMinCoord); + } +} diff --git a/hist/hist/src/THStack.cxx b/hist/hist/src/THStack.cxx index e85d5fd3a069767912ac1f382564f88a8c17898b..d522bf58d4e3db5aab6f54bd4960b6d3e9a8e5a5 100644 --- a/hist/hist/src/THStack.cxx +++ b/hist/hist/src/THStack.cxx @@ -88,7 +88,10 @@ End_Macro Note that picking is supported for all drawing modes. \since **ROOT version 6.07/07:** -Stacks of 2D histogram can also be painted as candle plots: +Stacks of 2D histograms can also be painted as candle plots: +\since **ROOT version 6.09/02:** +Stacks of 2D histograms can also be painted as violin plots, combinations of candle and +violin plots are possible as well: Begin_Macro(source) ../../../tutorials/hist/candleplotstack.C @@ -779,12 +782,13 @@ void THStack::Paint(Option_t *choptin) char *nostack = strstr(loption,"nostack"); char *nostackb = strstr(loption,"nostackb"); char *candle = strstr(loption,"candle"); + char *violin = strstr(loption,"violin"); // do not delete the stack. Another pad may contain the same object // drawn in stack mode! //if (nostack && fStack) {fStack->Delete(); delete fStack; fStack = 0;} - if (!opt.Contains("nostack") && (!opt.Contains("candle"))) BuildStack(); + if (!opt.Contains("nostack") && (!opt.Contains("candle")) && (!opt.Contains("violin"))) BuildStack(); Double_t themax,themin; if (fMaximum == -1111) themax = GetMaximum(option); @@ -880,7 +884,7 @@ void THStack::Paint(Option_t *choptin) char noption[32]; strlcpy(noption,loption,32); Int_t nhists = fHists->GetSize(); - if (nostack || candle) { + if (nostack || candle || violin) { lnk = (TObjOptLink*)fHists->FirstLink(); TH1* hAti; Double_t bo=0.03; @@ -893,7 +897,8 @@ void THStack::Paint(Option_t *choptin) TString indivOpt = lnk->GetOption(); indivOpt.ToLower(); if (nostackb) snprintf(loption,31,"%ssame%s b",noption,lnk->GetOption()); - else if (candle && indivOpt.Contains("candle")) snprintf(loption,31,"%ssame",lnk->GetOption()); + else if (candle && (indivOpt.Contains("candle") || indivOpt.Contains("violin"))) snprintf(loption,31,"%ssame",lnk->GetOption()); + else snprintf(loption,31,"%ssame%s",noption,lnk->GetOption()); } hAti = (TH1F*)(fHists->At(i)); @@ -902,7 +907,7 @@ void THStack::Paint(Option_t *choptin) hAti->SetBarOffset(bo); bo += bw; } - if (candle) { + if (candle || violin) { float candleSpace = 1./(nhists*2); float candleOffset = - 1./2 + candleSpace + 2*candleSpace*i; candleSpace *= 1.66; //width of the candle per bin: 1.0 means space is as great as the candle, 2.0 means there is no space diff --git a/hist/histpainter/inc/Hoption.h b/hist/histpainter/inc/Hoption.h index 91d295ba402d9f29510d616a410930d87cc86b2b..5503921e81cf6c2d9ea4a5e2cf4aa53b0da36543 100644 --- a/hist/histpainter/inc/Hoption.h +++ b/hist/histpainter/inc/Hoption.h @@ -49,8 +49,7 @@ typedef struct Hoption_t { int Text; ///< "TEXT" Draw 2D plot with the content of each cell. int Tri; ///< "TRI" Draw 2D plot with Delaunay triangles. int Pie; ///< "PIE" Draw 1D plot as a pie chart. - int Candle; ///< "CANDLE" Draw a 2D histogram as candle/box plot. - int Violin; ///< "VIOLIN" Draw a 2D histogram as violin plot. + long Candle; ///< "CANDLE" Draw a 2D histogram as candle/box plot or violin plot (also with "VIOLIN"). int System; ///< type of coordinate system(1=car,2=pol,3=cyl,4=sph,5=psr) int Zscale; ///< "Z" to display the Z scale (color palette) int FrontBox; ///< = 0 to suppress the front box diff --git a/hist/histpainter/inc/THistPainter.h b/hist/histpainter/inc/THistPainter.h index cc3c40f397d3187fa3465648fdadb2df778ee9a8..0e7267a919dca0cc5be00cbd2b49a7611b9ad621 100644 --- a/hist/histpainter/inc/THistPainter.h +++ b/hist/histpainter/inc/THistPainter.h @@ -89,7 +89,6 @@ public: virtual void PaintBarH(Option_t *option); virtual void PaintBoxes(Option_t *option); virtual void PaintCandlePlot(Option_t *option); - virtual void PaintViolinPlot(Option_t *option); virtual void PaintColorLevels(Option_t *option); virtual void PaintColorLevelsFast(Option_t *option); virtual std::vector<THistRenderingRegion> ComputeRenderingRegions(TAxis *pAxis, Int_t nPixels, bool isLog); diff --git a/hist/histpainter/src/THistPainter.cxx b/hist/histpainter/src/THistPainter.cxx index 0500e3b12ad08936c552b4f8bfe211884d9909a5..e601cc4bf9ea45589ecb141b1c4ec8fefc2c842b 100644 --- a/hist/histpainter/src/THistPainter.cxx +++ b/hist/histpainter/src/THistPainter.cxx @@ -94,8 +94,9 @@ - [The ARRow option](#HP12) - [The BOX option](#HP13) - [The COLor option](#HP14) -- [The CANDLE option](#HP140) -- [The VIOLIN option](#HP141) +- [The CANDLE and VIOLIN options](#HP140) + - [The CANDLE option](#HP140a) + - [The VIOLIN option](#HP140b) - [The TEXT and TEXTnn Option](#HP15) - [The CONTour options](#HP16) - [The LIST option](#HP16a) @@ -276,9 +277,13 @@ using `TH1::GetOption`: | "CANDLE" | Draw a candle plot along X axis.| | "CANDLEX" | Same as "CANDLE".| | "CANDLEY" | Draw a candle plot along Y axis.| +| "CANDLEXn"| Draw a candle plot along X axis. Different candle-styles with n from 1 to 6.| +| "CANDLEYn"| Draw a candle plot along Y axis. Different candle-styles with n from 1 to 6.| | "VIOLIN" | Draw a violin plot along X axis.| | "VIOLINX" | Same as "VIOLIN".| | "VIOLINY" | Draw a violin plot along Y axis.| +| "VIOLINXn"| Draw a violin plot along X axis. Different violin-styles with n being 1 or 2.| +| "VIOLINYn"| Draw a violin plot along Y axis. Different violin-styles with n being 1 or 2.| | "CONT" | Draw a contour plot (same as CONT0).| | "CONT0" | Draw a contour plot using surface colors to distinguish contours.| | "CONT1" | Draw a contour plot using line styles to distinguish contours.| @@ -1107,8 +1112,71 @@ and COLZ options. There is one major difference and that concerns the treatment bins with zero content. The COL2 and COLZ2 options color these bins the color of zero. -### <a name="HP140"></a> The CANDLE option +### <a name="HP140"></a> The CANDLE and VIOLIN options +The mechanism behind Candle plots and Violin plots is very similar. Because of this they are +implemented in the same class TCandle. The keywords CANDLE or VIOLIN will initiate the drawing of +the corresponding plots. Followed by the keyword the user can select a plot direction (X or V for +vertical projections, or Y or H for horizontal projections) and/or predefined definitions +(1-6 for candles, 1-2 for violins). The order doesn't matter. Default is X and 1. + +Instead of using the predefined representations, the candle and violin parameters can be +changed individually. In that case the option have the following form: + + CANDLEX(<option-string>) + CANDLEY(<option-string>) + VIOLINX(<option-string>) + VIOLINY(<option-string>). + +All zeros at the beginning of `option-string` can be omitted. + +`option-string` consists eight values, defined as follow: + + "CANDLEX(zhpawMmb)" + +Where: + + - `b = 0`; no box drawn + - `b = 1`; the box is drawn. As the candle-plot is also called a box-plot it + makes sense in the very most cases to always draw the box + - `b = 2`; draw a filled box with border + + - `m = 0`; no median drawn + - `m = 1`; median is drawn as a line + - `m = 2`; median is drawn with errors (notches) + - `m = 3`; median is drawn as a circle + + - `M = 0`; no mean drawn + - `M = 1`; mean is drawn as a dashed line + - `M = 3`; mean is drawn as a circle + + - `w = 0`; no whisker drawn + - `w = 1`; whisker is drawn to end of distribution. + - `w = 2`; whisker is drawn to max 1.5*iqr + + - `a = 0`; no anchor drawn + - `a = 1`; the anchors are drawn + + - `p = 0`; no points drawn + - `p = 1`; only outliers are drawn + - `p = 2`; all datapoints are drawn + - `p = 3`: all datapoints are drawn scattered + + - `h = 0`; no histogram is drawn + - `h = 1`; histogram at the left or bottom side is drawn + - `h = 2`; histogram at the right or top side is drawn + - `h = 3`; histogram at left and right or top and bottom (violin-style) is drawn + + - `z = 0`; no zero indicator line is drawn + - `z = 1`; zero indicator line is drawn. + +As one can see all individual options for both candle and violin plots can be accessed by this +mechanism. In deed the keywords CANDLE(<option-string>) and VIOLIN(<option-string>) have the same +meaning. So you can parametrise an option-string for a candle plot and use the keywords VIOLIN and +vice versa, if you wish. + + +#### <a name="HP140a"></a> The CANDLE option <a href="http://en.wikipedia.org/wiki/Box_plot">A Candle plot</a> (also known as a "box plot" or "whisker plot") was invented in 1977 by John Tukey. It is a convenient @@ -1122,9 +1190,9 @@ way to describe graphically a data distribution (D) with only five numbers: In this implementation a TH2 is considered as a collection of TH1 along X (option `CANDLE` or `CANDLEX`) or Y (option `CANDLEY`). -Each TH1 is represented as a candle plot. +Each TH1 is represented as one candle. -Begin_Macro +Begin_Macro(source) ../../../tutorials/hist/candleplotwhiskers.C End_Macro @@ -1265,6 +1333,10 @@ Depending on the configuration the points can have different meanings: drawn as dots and will be scattered within the width of the candle. The color of the points will be the color of the candle-chart. +##### Other Options +Is is possible to combine all options of candle and violin plots with each other. E.g. a box-plot +with a histogram. + #### How to use the candle-plots drawing option @@ -1283,16 +1355,6 @@ There are six predefined candle-plot representations: - "CANDLEX6": Like candle2 but showing all datapoints scattered. For huge datasets -Of course "CANDLEY" works as well. X shows vertical candles, Y shows -horizontal candles - -The option "CANDLE" is equivalent to "CANDLE1X" or "CANDLEX". - -The option "CANDLEY" is equivalent to "CANDLEY1" - -There is no difference between options "CANDLE1X" and "CANDLEX1". - -Instead of "X" or "Y" one can use "V" or "H" The following picture shows how the six predefined representations look. @@ -1319,51 +1381,7 @@ Begin_Macro } End_Macro -Instead of using the predefined representations, the candle parameters can be -changed individually. In that case the option has the following form: - CANDLEX(<option-string>) - -or - - CANDLEY(<option-string>). - -All zeros at the beginning of `option-string` can be omitted. - -`option-string` consists six values, defined as follow: - - "CANDLEX(pawMmb)" - -Where: - - - `b = 0`; no box drawn - - `b = 1`; the box is drawn. As the candle-plot is also called a box-plot it - makes sense in the very most cases to always draw the box. - `b = 2`; draw a filled box with border. - - - `m = 0`; no median drawn - - `m = 1`; median is drawn as a line - - `m = 2`; median is drawn with errors (notches) - - `m = 3`; median is drawn as a circle - - - `M = 0`; no mean drawn - - `M = 1`; mean is drawn as a dashed line - - `M = 3`; mean is drawn as a circle - - - `w = 0`; no whisker drawn - - `w = 1`; whisker is drawn to end of distribution. - - `w = 2`; whisker is drawn to max 1.5*iqr - - - `a = 0`; no anchor drawn - - `a = 1`; the anchors are drawn - - - `p = 0`; no points drawn - - `p = 1`; only outliers are drawn - - `p = 2`; all datapoints are drawn - - `p = 3`: all datapoints are drawn scattered - -The values on the left of `option-string` are in the middle of the candle, -the values on the right of `option-string` are in the outer part of the candle. #### Example 1 Box and improved whisker, no mean, no median, no anchor no outliers @@ -1385,8 +1403,7 @@ Begin_Macro(source) ../../../tutorials/hist/candleplot.C End_Macro -### <a name="HP141"></a> The VIOLIN option - +#### <a name="HP140b"></a> The VIOLIN option <a href="http://en.wikipedia.org/wiki/Violin_plot">A violin plot</a> is a candle plot that also encodes the pdf information at each point. @@ -1398,6 +1415,47 @@ and two lines. In this implementation a TH2 is considered as a collection of TH1 along X (option `VIOLIN` or `VIOLINX`) or Y (option `VIOLINY`). +#### What a violin is made of + +\since **ROOT version 6.09/02** + +##### The histogram +The histogram is typically drawn to both directions with respect to the middle-line of the +corresponding bin. This can be achieved by using h=3. It is possible to draw a histogram only to +one side (h=1, or h=2). +The maximum number of bins in the histogram is limited to 500, if the number of bins in the used +histogram is higher it will be rebinned automatically. The maximum height of the histogram can +be modified by using SetBarWidth() and the position can be changed with SetBarOffset(). +A solid fill style is recommended. + +##### The zero indicator line +Typical for violin charts is a line in the background over the whole histogram indicating +the bins with zero entries. The zero indicator line can be activated with z=1. The line color +will always be the same as the fill-color of the histogram. + +##### The Mean +The Mean is illustrated with the same mechanism as used for candle plots. Usually a circle is used. + +##### Whiskers +The whiskers are illustrated by the same mechanism as used for candle plots. There is only one +difference. When using the simple whisker definition (w=1) and the zero indicator line (z=1), then +the whiskers will be forced to be solid (usually hashed) + +##### Points +The points are illustrated by the same mechanism as used for candle plots. E.g. VIOLIN2 uses +better whisker definition (w=2) and outliers (p=1). + +##### Other options +It is possible to combine all options of candle or violin plots with each other. E.g. a violin plot +including a box-plot. + +#### How to use the violin-plots drawing option + +There are two predefined violin-plot representations: + - "VIOLINX1": Standard violin (histogram, mean, whisker over full distribution, + zero indicator line) + - "VIOLINX2": Line VIOLINX1 both with better whisker definition + outliers. + A solid fill style is recommended for this plot (as opposed to a hollow or hashed style). @@ -1427,6 +1485,12 @@ Begin_Macro(source) } End_Macro +The next example illustrates a time development of a certain value: + +Begin_Macro(source) +../../../tutorials/hist/candledecay.C +End_Macro + ### <a name="HP15"></a> The TEXT and TEXTnn Option @@ -3509,7 +3573,7 @@ Int_t THistPainter::MakeChopt(Option_t *choptin) Hoption.Char = Hoption.Color = Hoption.Contour = Hoption.Logx = 0; Hoption.Logy = Hoption.Logz = Hoption.Lego = Hoption.Surf = 0; Hoption.Off = Hoption.Tri = Hoption.Proj = Hoption.AxisPos = 0; - Hoption.Spec = Hoption.Pie = Hoption.Candle = Hoption.Violin = 0; + Hoption.Spec = Hoption.Pie = Hoption.Candle = 0; // special 2D options Hoption.List = 0; @@ -3605,11 +3669,9 @@ Int_t THistPainter::MakeChopt(Option_t *choptin) l = strstr(chopt,"VIOLIN"); if (l) { + TCandle candle; + Hoption.Candle = candle.ParseOption(l); Hoption.Scat = 0; - Hoption.Violin = 1; - strncpy(l," ",6); - if (l[6] == 'X') { Hoption.Violin = 1; l[6] = ' '; } - if (l[6] == 'Y') { Hoption.Violin = 2; l[6] = ' '; } } l = strstr(chopt,"LEGO"); @@ -4757,7 +4819,7 @@ void THistPainter::PaintBoxes(Option_t *) //////////////////////////////////////////////////////////////////////////////// -/// [Control function to draw a 2D histogram as a candle (box) plot.](#HP14) +/// [Control function to draw a 2D histogram as a candle (box) plot or violin plot](#HP14) void THistPainter::PaintCandlePlot(Option_t *) { @@ -4785,7 +4847,7 @@ void THistPainter::PaintCandlePlot(Option_t *) if (hproj->GetEntries() !=0) { Double_t width = fH->GetBarWidth(); Double_t offset = fH->GetBarOffset()*binWidth; - if (width > 0.999) width = standardCandleWidth; + if (width > 0.999 && width < 1.001) width = standardCandleWidth; myCandle.SetAxisPosition(binPosX+binWidth/2. + offset); myCandle.SetWidth(width*binWidth); myCandle.SetHistogram(hproj); @@ -4800,7 +4862,7 @@ void THistPainter::PaintCandlePlot(Option_t *) if (hproj->GetEntries() !=0) { Double_t width = fH->GetBarWidth(); Double_t offset = fH->GetBarOffset()*binWidth; - if (width > 0.999) width = standardCandleWidth; + if (width > 0.999 && width < 1.001) width = standardCandleWidth; myCandle.SetAxisPosition(binPosY+binWidth/2. + offset); myCandle.SetWidth(width*binWidth); myCandle.SetHistogram(hproj); @@ -4810,117 +4872,7 @@ void THistPainter::PaintCandlePlot(Option_t *) } } -//////////////////////////////////////////////////////////////////////////////// -/// [Control function to draw a 2D histogram as a violin plot](#HP141) - -void THistPainter::PaintViolinPlot(Option_t *) -{ - Double_t x,y,w; - Double_t bw, bcen, bcon; - Double_t xpm[1], ypm[1]; - - TH1D *hp; - TH2D *h2 = (TH2D*)fH; - Double_t *quantiles = new Double_t[5]; - quantiles[0]=0.; quantiles[1]=0.; quantiles[2] = 0.; quantiles[3] = 0.; quantiles[4] = 0.; - Double_t *prob = new Double_t[5]; - prob[0]=1E-15; prob[1]=0.25; prob[2]=0.5; prob[3]=0.75; prob[4]=1-1E-15; - - Style_t fillsav = h2->GetFillStyle(); - Style_t colsav = h2->GetFillColor(); - Style_t linesav = h2->GetLineStyle(); - Style_t widthsav = h2->GetLineWidth(); - Style_t pmssav = h2->GetMarkerStyle(); - - if (h2->GetFillColor() == 0) h2->SetFillStyle(0); - - h2->SetMarkerStyle(pmssav); - h2->TAttLine::Modify(); - h2->TAttFill::Modify(); - h2->TAttMarker::Modify(); - - // Violin plot along X - if (Hoption.Violin == 1) { - for (Int_t i=Hparam.xfirst; i<=Hparam.xlast; i++) { - x = fXaxis->GetBinCenter(i); - w = fXaxis->GetBinWidth(i); - hp = h2->ProjectionY("_px", i, i); - if (hp->GetEntries() !=0 && hp->GetMaximum()!=0) { - hp->Scale(1.0/hp->Integral()); - hp->Scale(w/hp->GetMaximum()); - hp->GetQuantiles(5, quantiles, prob); - ypm[0] = hp->GetMean(); - - TAxis *ax = hp->GetXaxis(); - for(Int_t j=ax->GetFirst(); j<ax->GetLast(); ++j){ - bw = ax->GetBinWidth(j); - bcen = ax->GetBinCenter(j); - bcon = hp->GetBinContent(j); - gPad->PaintBox(x-0.5*bcon, bcen-0.5*bw, x+0.5*bcon, bcen+0.5*bw); - } - - h2->SetLineWidth(widthsav); - h2->TAttLine::Modify(); - - h2->SetLineStyle(linesav); - h2->TAttLine::Modify(); - gPad->PaintLine(x, quantiles[3], x, quantiles[4]); - gPad->PaintLine(x, quantiles[0], x, quantiles[1]); - - xpm[0] = x; - gPad->PaintPolyMarker(1,xpm,ypm); - } - } - // Violin plot along Y - } else { - for (Int_t i=Hparam.yfirst; i<=Hparam.ylast; i++) { - y = fYaxis->GetBinCenter(i); - w = fYaxis->GetBinWidth(i); - hp = h2->ProjectionX("_py", i, i); - if (hp->GetEntries() !=0 && hp->GetMaximum()!=0) { - hp->Scale(1.0/hp->Integral()); - hp->Scale(w/hp->GetMaximum()); - hp->GetQuantiles(5, quantiles, prob); - xpm[0] = hp->GetMean(); - - h2->SetLineWidth(1); - h2->TAttLine::Modify(); - TAxis *ax = hp->GetXaxis(); - for(Int_t j=ax->GetFirst(); j<ax->GetLast(); ++j){ - bw = ax->GetBinWidth(j); - bcen = ax->GetBinCenter(j); - bcon = hp->GetBinContent(j); - gPad->PaintBox(bcen-0.5*bw, y-0.5*bcon, bcen+0.5*bw, y+0.5*bcon); - } - - hp->GetQuantiles(5, quantiles, prob); - xpm[0] = hp->GetMean(); - - h2->SetLineWidth(widthsav); - h2->SetLineStyle(2); - h2->TAttLine::Modify(); - gPad->PaintLine(quantiles[3], y, quantiles[4], y); - gPad->PaintLine(quantiles[0], y, quantiles[1], y); - - ypm[0] = y; - gPad->PaintPolyMarker(1,xpm,ypm); - } - } - } - - h2->SetFillStyle(fillsav); - h2->SetFillColor(colsav); - h2->SetLineStyle(linesav); - h2->SetMarkerStyle(pmssav); - h2->SetLineWidth(widthsav); - h2->TAttFill::Modify(); - h2->TAttLine::Modify(); - h2->TAttMarker::Modify(); - - delete [] prob; - delete [] quantiles; -} //////////////////////////////////////////////////////////////////////////////// /// Returns the rendering regions for an axis to use in the COL2 option @@ -5230,6 +5182,7 @@ void THistPainter::PaintColorLevelsFast(Option_t*) //////////////////////////////////////////////////////////////////////////////// /// [Control function to draw a 2D histogram as a color plot.](#HP14) + void THistPainter::PaintColorLevels(Option_t*) { Double_t z, zc, xk, xstep, yk, ystep, xlow, xup, ylow, yup; @@ -8696,7 +8649,6 @@ void THistPainter::PaintTable(Option_t *option) if (Hoption.Text) PaintText(option); if (Hoption.Error >= 100) Paint2DErrors(option); if (Hoption.Candle) PaintCandlePlot(option); - if (Hoption.Violin) PaintViolinPlot(option); } if (Hoption.Lego) PaintLego(option); if (Hoption.Surf && !Hoption.Contour) PaintSurface(option); diff --git a/tutorials/hist/candledecay.C b/tutorials/hist/candledecay.C new file mode 100644 index 0000000000000000000000000000000000000000..a408e97eebf5c1adf367e91d555364d724b76950 --- /dev/null +++ b/tutorials/hist/candledecay.C @@ -0,0 +1,38 @@ +/// \file +/// \ingroup tutorial_hist +/// \notebook +/// Candle Decay, illustrate a time development of a certain value. +/// +/// \macro_image +/// \macro_code +/// +/// \author Georg Troska + +void candledecay() +{ + TCanvas *c1 = new TCanvas("c1","Candle Decay",800,600); + c1->Divide(2,1); + TRandom *rand = new TRandom(); + TH2I *h1 = new TH2I("h1","Decay",1000,0,1000,20,0,20); + + float myRand; + for (int i = 0; i < 17; i++) { + for (int j = 0; j < 1000000; j++) { + myRand = rand->Gaus(350+i*5,5+5*i); + h1->Fill(myRand,i); + } + } + h1->SetBarWidth(4); + h1->SetFillStyle(0); + h1->SetFillColor(kGray); + h1->SetLineColor(kBlue); + h1->GetYaxis()->SetTitle("time"); + h1->GetXaxis()->SetTitle("probability density"); + + c1->cd(1); + h1->Draw("violiny(12000000)"); + c1->cd(2); + TH2I *h2 = (TH2I*)h1->Clone("h2"); + h2->SetBarWidth(0.8); + h2->DrawCopy("candley2"); +} diff --git a/tutorials/hist/candlehisto.C b/tutorials/hist/candlehisto.C new file mode 100644 index 0000000000000000000000000000000000000000..1e3750cb8e4d989c267654347f9fae839a5dc52f --- /dev/null +++ b/tutorials/hist/candlehisto.C @@ -0,0 +1,69 @@ +/// \file +/// \ingroup tutorial_hist +/// Example showing how to combine the various candle plot options. +/// +/// \macro_image +/// \macro_code +/// +/// \author Georg Troska + +void candlehisto() +{ + TCanvas *c1 = new TCanvas("c1","Candle Presets",800,600); + c1->Divide(3,2); + + TRandom *rand = new TRandom(); + TH2I *h1 = new TH2I("h1","Sin",18,0,360,100,-1.5,1.5); + h1->GetXaxis()->SetTitle("Deg"); + + float myRand; + for (int i = 0; i < 360; i+=10) { + for (int j = 0; j < 100; j++) { + myRand = rand->Gaus(sin(i*3.14/180),0.2); + h1->Fill(i,myRand); + } + } + + for (int i = 1; i < 7; i++) { + c1->cd(i); + char str[16]; + sprintf(str,"CANDLEX%d",i); + TH2I * myhist = (TH2I*)h1->DrawCopy(str); + myhist->SetTitle(str); + } + + TCanvas *c2 = new TCanvas("c2","Violin Presets",800,300); + c2->Divide(2,1); + + for (int i = 1; i < 3; i++) { + c2->cd(i); + char str[16]; + sprintf(str,"VIOLINX%d",i); + TH2I * myhist = (TH2I*)h1->DrawCopy(str); + myhist->SetFillColor(kGray+2); + } + + TCanvas *c3 = new TCanvas("c3","Playing with candle and violin-options",800,600); + c3->Divide(3,2); + char myopt[6][16] = {"1000000","2000000","3000000","1112111","112111","112111"}; + for (int i = 0; i < 6; i++) { + c3->cd(i+1); + char str[16]; + sprintf(str, "candlex(%s)",myopt[i]); + TH2I * myhist = (TH2I*)h1->DrawCopy(str); + myhist->SetFillColor(kYellow); + if (i == 4) { + TH2I * myhist2 = (TH2I*)h1->DrawCopy("candlex(1000000) same"); + myhist2->SetFillColor(kRed); + } + if (i == 5) { + myhist->SetBarWidth(0.2); + myhist->SetBarOffset(0.25); + TH2I * myhist2 = (TH2I*)h1->DrawCopy("candlex(2000000) same"); + myhist2->SetFillColor(kRed); + myhist2->SetBarWidth(0.6); + myhist2->SetBarOffset(-0.5); + } + myhist->SetTitle(str); + } +} diff --git a/tutorials/hist/candleplot.C b/tutorials/hist/candleplot.C index 38f8199d536feb977ca8bf3c86fd6dc29a96d0c6..406c4f0a06156df650fdf08f2a53575a6562c9e1 100644 --- a/tutorials/hist/candleplot.C +++ b/tutorials/hist/candleplot.C @@ -11,7 +11,7 @@ void candleplot() { gStyle->SetTimeOffset(0); - TRandom *randnum = new TRandom(); + TRandom *rand = new TRandom(); TDatime *dateBegin = new TDatime(2010,1,1,0,0,0); TDatime *dateEnd = new TDatime(2011,1,1,0,0,0); @@ -25,22 +25,25 @@ void candleplot() { float Rand; for (int i = dateBegin->Convert(); i < dateEnd->Convert(); i+=86400*30) { for (int j = 0; j < 1000; j++) { - Rand = randnum->Gaus(500+sin(i/10000000.)*100,50); h1->Fill(i,Rand); - Rand = randnum->Gaus(500+sin(i/11000000.)*100,70); h2->Fill(i,Rand); + Rand = rand->Gaus(500+sin(i/10000000.)*100,50); h1->Fill(i,Rand); + Rand = rand->Gaus(500+sin(i/11000000.)*100,70); h2->Fill(i,Rand); } } h1->SetBarWidth(0.4); h1->SetBarOffset(-0.25); + h1->SetFillColor(kYellow); + h1->SetFillStyle(1001); h2->SetBarWidth(0.4); h2->SetBarOffset(0.25); h2->SetLineColor(kRed); + h2->SetFillColor(kGreen); TCanvas *c1 = new TCanvas(); h1->Draw("candle2"); h2->Draw("candle3 same"); - gPad->BuildLegend(0.6,0.7,0.7,0.8); + gPad->BuildLegend(0.78,0.695,0.980,0.935,"","f"); } diff --git a/tutorials/hist/candleplotoption.C b/tutorials/hist/candleplotoption.C index 85f4f4d0750e8ce060d72422f6d7bb59755caddd..a1f03c4a872f9ddcfe042e93b0424161b9309d6f 100644 --- a/tutorials/hist/candleplotoption.C +++ b/tutorials/hist/candleplotoption.C @@ -10,7 +10,7 @@ void candleplotoption() { - TCanvas *c1 = new TCanvas("c1","Candle Presets",1200,800); + TCanvas *c1 = new TCanvas("c1","Candle Presets",800,600); c1->Divide(3,2); TRandom *randnum = new TRandom(); @@ -31,7 +31,7 @@ void candleplotoption() myhist->SetTitle(str); } - TCanvas *c2 = new TCanvas("c2","Candle Individual",1200,800); + TCanvas *c2 = new TCanvas("c2","Candle Individual",800,600); c2->Divide(4,4); char myopt[16][8] = {"0","1","11","21","31","30","111","311","301","1111","2321","12111","112111","212111","312111"}; for (int i = 0; i < 15; i++) { diff --git a/tutorials/hist/candleplotstack.C b/tutorials/hist/candleplotstack.C index 821aedcc6a8c720e6e0b1b0b8e0379aa9294a6c8..2cf6be624b6e84408fae734692d49536fa92353d 100644 --- a/tutorials/hist/candleplotstack.C +++ b/tutorials/hist/candleplotstack.C @@ -14,28 +14,31 @@ void candleplotstack() TRandom *randnum = new TRandom(); TDatime *dateBegin = new TDatime(2010,1,1,0,0,0); TDatime *dateEnd = new TDatime(2011,1,1,0,0,0); - TH2I *h1 = new TH2I("h1","Machine A",12,dateBegin->Convert(),dateEnd->Convert(),1000,0,1000); - TH2I *h2 = new TH2I("h2","Machine B",12,dateBegin->Convert(),dateEnd->Convert(),1000,0,1000); - TH2I *h3 = new TH2I("h3","Machine C",12,dateBegin->Convert(),dateEnd->Convert(),1000,0,1000); + int bins = 1000; + TH2I *h1 = new TH2I("h1","Machine A",6,dateBegin->Convert(),dateEnd->Convert(),bins,0,1000); + TH2I *h2 = new TH2I("h2","Machine B",6,dateBegin->Convert(),dateEnd->Convert(),bins,0,1000); + TH2I *hsum = new TH2I("h4","Sum",6,dateBegin->Convert(),dateEnd->Convert(),bins,0,1000); float Rand; for (int i = dateBegin->Convert(); i < dateEnd->Convert(); i+=86400*30) { for (int j = 0; j < 1000; j++) { - Rand = randnum->Gaus(500+sin(i/10000000.)*100,50); h1->Fill(i,Rand); - Rand = randnum->Gaus(500+sin(i/11000000.)*100,70); h2->Fill(i,Rand); - Rand = randnum->Gaus(500+sin(i/11000000.)*100,90); h3->Fill(i,Rand); + Rand = randnum->Gaus(500+sin(i/10000000.)*100,50); h1->Fill(i,Rand); hsum->Fill(i,Rand); + Rand = randnum->Gaus(500+sin(i/12000000.)*100,50); h2->Fill(i,Rand); hsum->Fill(i,Rand); } } h2->SetLineColor(kRed); - h3->SetLineColor(kRed+3); + hsum->SetFillColor(kGreen); TCanvas *c1 = new TCanvas(); - THStack *hs = new THStack("hs","Machine A+B+C"); + THStack *hs = new THStack("hs","Machine A+B"); hs->Add(h1); - hs->Add(h2); - hs->Add(h3,"candle2"); + hs->Add(h2,"candle2"); + hs->Add(hsum, "violin1"); hs->Draw("candle3"); + hs->GetXaxis()->SetNdivisions(410); + + gPad->SetGrid(1,0); hs->GetXaxis()->SetTimeDisplay(1); hs->GetXaxis()->SetTimeFormat("%m/%y"); diff --git a/tutorials/hist/candleplotwhiskers.C b/tutorials/hist/candleplotwhiskers.C index cd1f99c5d9dfc412af38393931f1ecf816174812..3a339c65cd778ded22526690c32a324ba81bbaa1 100644 --- a/tutorials/hist/candleplotwhiskers.C +++ b/tutorials/hist/candleplotwhiskers.C @@ -43,6 +43,7 @@ void candleplotwhiskers() { mygaus_1_right->SetLineColor(kGreen); c1->cd(1); h1->SetLineWidth(3); + h1->SetFillStyle(0); h1->Draw("candley2 scat"); c1->cd(2);