5
$\begingroup$

I have as example two data sets and want to make a combined histogram.

When I do the following I can hardly distinguish which histogram belongs to which data.

SeedRandom["1"];
data1 = RandomInteger[{1, 5}, 100];
data2 = RandomInteger[{0, 6}, 150];
Histogram[{data1, data2}, {1}]

enter image description here

How can I achive the following combined histograms:

A.

histogram of data1: red, transparent, solid edges

histogram of data2: blue, transparent, solid edges

B.

histogram of data1: red

histogram of data2: blue

overlap: green

C.

histogram of data1: solid

histogram of data2: dashed

$\endgroup$
  • $\begingroup$ That one can't distinguish overlapping histograms is why one shouldn't overlap histograms. Depending on the data you might want to try SmoothHistogram[{data1, data2}]. Or PairedHistogram[data1, data2]. $\endgroup$ – JimB Apr 28 '17 at 16:41
4
$\begingroup$

Cases A and C can be handled with appropriate ChartStyle settings:

Histogram[{data1, data2}, {1}, 
  ChartStyle -> {Opacity[.25, Red], Opacity[.25, Blue]}]

Mathematica graphics

Histogram[{data1, data2}, {1}, 
  ChartStyle -> {Red, Directive[EdgeForm[{Thick, Dashed}], Blue]}]

Mathematica graphics

Case B is difficult. The following trick uses HistogramList to get the bar heights and BarChart with appropriate inputs and options to get the desired look:

ClearAll[heightsF]
heightsF = Module[{binlims = {Union @@ (HistogramList[#, {1}][[1]] & /@ #)}, bars}, 
    bars = HistogramList[#, binlims][[2]] & /@ #; 
    Transpose[Join[bars, {Min @@@ Transpose[bars]}]]] &;

barheights = heightsF[{data1, data2}];
BarChart[barheights, ChartBaseStyle -> Opacity[1], 
 ChartStyle -> {Red, Blue, Green}, ChartLayout -> "Overlapped", BarSpacing -> {0, 0}] 

Mathematica graphics

$\endgroup$
  • $\begingroup$ Thanks a lot for the solution. I like very much case B. because it gives control about the overlapped color. $\endgroup$ – lio Apr 28 '17 at 19:50
  • $\begingroup$ @lio, my pleasure. Thank you for the accept. $\endgroup$ – kglr Apr 28 '17 at 19:51
3
$\begingroup$

If you don't want folks to understand two sets of data, overlapping histograms is probably the best way to do it.

If you do want folks to see the features of the data, here are two ways. First generate two datasets:

SeedRandom["1"];
data1 = RandomVariate[NormalDistribution[0, 1], 100];
data2 = RandomVariate[NormalDistribution[0.5, 0.5], 150];

A smoothed histogram (nonparametric density estimate) allows one to overlap multiple distributions and still allow one to identify the individual datasets:

SmoothHistogram[{data1, data2}]

Two smoothed histograms

If there are just two datasets, then showing both histograms side-by-side can be a good choice:

PairedHistogram[data1, data2]

Paired histograms

$\endgroup$
  • $\begingroup$ Thank you for your help. SmoothHistogram is very interesting. $\endgroup$ – lio Apr 28 '17 at 19:54
0
$\begingroup$

This is an answer from Wolfram Technical Support regarding histogram B in my question:

Styling Histograms

Using Options

Probably the easiest way to approach this is to use the option ChartStyle iwith opacity. The RGBColor function can be given with four arguments, with the fourth argument setting the opacity of the result. When there is overlap the colors will blend. Here is an example using your data sets.

SeedRandom["1"];
data1 = RandomInteger[{1, 5}, 100];
data2 = RandomInteger[{0, 6}, 150];
Histogram[{data1, data2}, {1},
 ChartStyle -> {RGBColor[1, 0, 0, .5], RGBColor[0, 1, 0, .5]}]

enter image description here

The difficulty here is that the user does not have control over the blend. With a careful choice of colors this can work well, but there are going to be cases where this does not give satisfactory results.

Creating Your Own Function

This approach is a little more work, but if you are intending to create a number of plots it can be worth the extra work. We will assume that the binning is consistent between sets and if you are dealing with set of integers this is not difficult to achieve.

HistogramList

The function HistogramList is a good place to start. This will return a list of bin boundaries and the counts for each of the intervals. Here we get the values for your example sets.

prts1 = HistogramList[data1]

{{0.5, 1.5, 2.5, 3.5, 4.5, 5.5}, {20, 13, 24, 16, 27}}

prts2 = HistogramList[data2]

{{-0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5}, {21, 18, 22, 24, 25, 19, 21}}

Note that the lists indicate the bins are consistent from one data set to the next.

Modifying the Histogram Lists

We begin by modifying the histogram lists to give the bounds of each interval followed by the set those values come from and the counts for the bin.

brs1 = Thread[{Partition[prts1[[1]], 2, 1], 1, prts1[[2]]}]

{{{0.5, 1.5}, 1, 20}, {{1.5, 2.5}, 1, 13}, {{2.5, 3.5}, 1, 24}, {{3.5, 4.5}, 1, 16}, {{4.5, 5.5}, 1, 27}}

brs2 = Thread[{Partition[prts2[[1]], 2, 1], 2, prts2[[2]]}]

{{{-0.5, 0.5}, 2, 21}, {{0.5, 1.5}, 2, 18}, {{1.5, 2.5}, 2, 22}, {{2.5, 3.5}, 2, 24}, {{3.5, 4.5}, 2, 
  25}, {{4.5, 5.5}, 2, 19}, {{5.5, 6.5}, 2, 21}}

Bringing the Sets Together

Here we use Join to bring the lists together and then create sublists for each bin.

totLs = GatherBy[Join[brs1, brs2], First]

{{{{0.5, 1.5}, 1, 20}, {{0.5, 1.5}, 2, 18}}, {{{1.5, 2.5}, 1, 13}, {{1.5, 2.5}, 2, 22}}, {{{2.5, 3.5}, 1, 
   24}, {{2.5, 3.5}, 2, 24}}, {{{3.5, 4.5}, 1, 16}, {{3.5, 4.5}, 2, 25}}, {{{4.5, 5.5}, 1, 27}, {{4.5, 5.5}, 
   2, 19}}, {{{-0.5, 0.5}, 2, 21}}, {{{5.5, 6.5}, 2, 21}}}

Note that some of the lists have a single bin while most have two counts, one for each of the sets.

Creating the Bars

Next we take the information for each bin and create rectangles for each count. There are four possible situations, the situation when only one set is present in the bin, one where the counts are the same for that bin, the case where the first data set is greater than the second and finally the situation where the second set has a count greater than the first. These cases are easily handled using the Which function. We include a style to use for each of the cases, the first data set, the second and both together.

makeRecs[ls_List, colLs : {(_RGBColor | _Directive ..)}] := Which[
  Length[ls] == 1, {colLs[[2]], Rectangle[{ls[[1, 1, 1]], 0}, {ls[[1, 1, 2]], ls[[1, 3]]}]},

  ls[[1, -1]] - ls[[2, -1]] == 0, {colLs[[3]], Rectangle[{ls[[1, 1, 1]], 0}, {ls[[1, 1, 2]], ls[[1, -1]]}]},

  Min[ls[[All, -1]]] == 
   ls[[1, -1]], {{colLs[[3]], 
    Rectangle[{ls[[1, 1, 1]], 0}, {ls[[1, 1, 2]], Min[ls[[All, -1]]]}]}, {colLs[[2]], 
    Rectangle[{ls[[1, 1, 1]], Min[ls[[All, -1]]]}, {ls[[1, 1, 2]], Max[ls[[All, -1]]]}]}},

  Max[ls[[All, -1]]] == 
   ls[[1, -1]], {{colLs[[3]], 
    Rectangle[{ls[[1, 1, 1]], 0}, {ls[[1, 1, 2]], Min[ls[[All, -1]]]}]}, {colLs[[1]], 
    Rectangle[{ls[[1, 1, 1]], Min[ls[[All, -1]]]}, {ls[[1, 1, 2]], Max[ls[[All, -1]]]}]}}]

Using the makeRecs Function

Here we use the makeRecs function on your data and assign the colors red to the first set, green to the second set and blue to both sets. Note this is applied to the combined list created above using Map.

Graphics[makeRecs[#, {Red, Green, Blue}] & /@ totLs,
 AspectRatio -> 1/GoldenRatio,
 Axes -> True]

enter image description here

A More Advanced Example

Here we use graphics options to dress up the plots. Framed and Legended are used to finish things off.

Framed[Legended[
  Graphics[makeRecs[#, Directive[#, EdgeForm[{Black, Thickness[Tiny]}]] & /@ {Yellow, Green, Orange}] & /@ 
    totLs,
   AspectRatio -> 1/GoldenRatio,
   Axes -> True,
   Frame -> True], SwatchLegend[{Yellow, Green, Orange}, {"data 1", "data 2", "both"}]],
 RoundingRadius -> 10]

enter image description here

Observations

The above steps can be combined into a single function to have a function similar to the built-in function. If more than two data sets are involved the number of cases increases quite rapidly so one should expect observable slowdowns as the number of sets increases. Because of that I have limited the number of sets to two. Also the above code requires the style of the bars to use RGBColor or the styles to be set using Directive as seen in the second example. If one wishes to take colors from the Color Schemes palette the RGBColor values can be obtained as illustrated below.

{ColorData[32][3], InputForm[ColorData[32][3]]}

 {RGBColor[0.7490196078431373, 0.43529411764705883`, 0.24705882352941178`], InputForm[
RGBColor[0.7490196078431373, 0.43529411764705883`, 0.24705882352941178`]]}

{ColorData["Rainbow"][.7], InputForm[ColorData["Rainbow"][.7]]}

{RGBColor[0.8083415999999999, 0.7110806000000001, 0.255976], InputForm[
RGBColor[0.8083415999999999, 0.7110806000000001, 0.255976]]}
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.