27
$\begingroup$

The spherical or circular data arises when we measure data in degree e.g wind direction, clock and compass etc. Then representation of such data we need to plot circular plot, circular histogram and rose diagram. How to plot this data? for example we have data

data={8,9,13,13,14,18,22,27,30,34,38,38,40,44,45,47,48,48,48,48,50,53,56,57,58,58,61,63, 64,64,64,65,65,68,70,73,78,78,78,83,83,88,88,88,90,92,92,93,95,96,98,100,103,106,113,118,138, 153,153,155,204,215,223,226,237,238,243,244,250,251,257,268,285,319,343,350} 

These are the 76 directions measured in degree clockwise from north. The specimen of circular plot, circular histogram and rose diagram are given below

Circular Plot data given above Circular Plot (data given above) Circular Histogram

Circular Histogram (not above data)

Rose Diagram Rose Diagram (not above data)

Note: Any appropriate interval is to be taken for circular histogram from the given data.

$\endgroup$
4
  • 2
    $\begingroup$ Surely the answers to this question from yesterday (which you have no doubt seen) address your query. mathematica.stackexchange.com/questions/31257/… $\endgroup$ Commented Aug 29, 2013 at 13:15
  • $\begingroup$ @DavidCarraher: I see your refer Q&A, also I follow it tomorrow too specially your answer. But Dont see any similarity of wind rose with circular plot, infect these observations are angels should be on the circumstances of the circle with respective angle. $\endgroup$ Commented Aug 29, 2013 at 13:34
  • 2
    $\begingroup$ @Azeem David is referring to the sector charts in Kuba's and Anon's answers $\endgroup$ Commented Aug 29, 2013 at 15:03
  • $\begingroup$ ListPolarPlot allows for inputs as ordered pairs consisting of (angle, value). You could Tally by angle or do BinCount s by angle intervals. The output can be displayed in various ways. You could do the same with SectorChart but would need to massage the data more than with ListPolarPlot. $\endgroup$ Commented Aug 29, 2013 at 15:16

6 Answers 6

24
$\begingroup$

The function f takes as arguments the raw, unbinned data, the number of sectors, and a boolean parameter to indicate whether polar gridlines are to be drawn.

data = {8, 9, 13, 13, 14, 18, 22, 27, 30, 34, 38, 38, 40, 44, 45, 47, 48, 48, 48, 48, 50, 53, 56, 57, 58, 58, 61, 63, 64, 64, 64, 65, 65, 68, 70, 73, 78, 78, 78, 83, 83, 88, 88, 88, 90, 92, 92, 93, 95, 96, 98, 100, 103, 106, 113, 118, 138, 153, 153, 155, 204, 215, 223, 226,237, 238, 243, 244, 250, 251, 257, 268, 285, 319, 343, 350} f[dat_,nSectors_,polarGridLinesQ_]:=Module[{binwidth=360/nSectors}, SectorChart[Thread[{ConstantArray[1,360/binwidth],BinCounts[data,binwidth]}], PolarAxes->If[polarGridLinesQ,{True,True},{False,True}], PolarTicks->{If[polarGridLinesQ,"Degrees",None],Automatic}, PolarGridLines->If[polarGridLinesQ,{Table[2Pi k/nSectors+Pi/4,{k,1,nSectors}],Automatic},{None,None}], SectorOrigin->{Pi/2,"Clockwise"}]] 

With 5 sectors

GraphicsGrid[{{f[data, 5, True], f[data, 5, False]}}, ImageSize -> Large] 

five sectors


With 25 sectors

GraphicsGrid[{{f[data, 25, True], f[data, 25, False]}}, ImageSize -> Large] 

twenty five sectors

$\endgroup$
2
  • $\begingroup$ Please use bins=5 or 25,75,100 instead of 15. $\endgroup$ Commented Aug 29, 2013 at 17:39
  • $\begingroup$ I used 5 and 25 bins (i.e. sectors) as examples. $\endgroup$ Commented Sep 10, 2015 at 0:57
25
$\begingroup$

Here's my contribution for the first type of chart:

bins = Tally @ Ceiling[data, 5]; labelfn = Text[HoldForm[# °], 8 {Sin[# °], Cos[# °]}] &; ptfn = Rotate[Point @ Thread @ {10 + Range@#2, 0}, Pi/2 - # °, {0, 0}] &; linefn = Rotate[Line[{{9.5, 0}, {10, 0}}], Pi/2 - # °, {0, 0}] &; Graphics[{ Circle[{0, 0}, 10], labelfn /@ {0, 90, 180, 270}, linefn /@ Range[0, 355, 5], PointSize[0.02], ptfn @@@ bins }] 

enter image description here

Sizes are hard-coded which is never the best, but it's a start.

I don't have HistogramList is v7 so I used Tally for brevity.

Kuba's V9 edit:

bins = {MovingAverage[#, 2], #2} & @@ HistogramList[data, {0, 360, 3.1}] // Transpose; ptfn = Rotate[Point@Thread@{10 + Range@#2, 0}, Pi/2 - # °, {0, 0}] &; linefn = Rotate[Line[{{9.5, 0}, {10, 0}}], Pi/2 - # °, {0, 0}] &; Graphics[{Circle[{0, 0}, 10], labelfn /@ {0, 90, 180, 270}, linefn /@ Range[0, 355, 10], PointSize[0.02], ptfn @@@ bins }] 

enter image description here

$\endgroup$
2
  • $\begingroup$ @Kuba Could you please add v9 code for HistogramList to my answer? I mean, show how to convert it to bins as I used? $\endgroup$ Commented Aug 30, 2013 at 5:07
  • $\begingroup$ I couldn't find the best width for bins and it does not reproduce exactly the same graph as in the question. Moreover i think there is something missing in your Tally method, take a look at 350-10 interval. $\endgroup$ Commented Aug 30, 2013 at 6:25
19
$\begingroup$

Here's my take at replicating your circular plot. To get the ticks right isn't as easy as one might think, there is no option to put the ticks on the inside of the circle. The ticks will have to be produced manually...

CircularDotHistogram[data_, n_, clockwise_: True] := Module[{hist, pts, deg2rad, angdata}, hist = HistogramList[data, n][[2]]; deg2rad[deg_, clock_] := If[clock, (5/2) Pi - (2 Pi/n) deg, (2 Pi/n) deg]; pts[maxval_, {degree_}] := {deg2rad[ degree, True], 10 + #} & /@ Range[maxval]; angdata = MapIndexed[pts, hist]; ListPolarPlot[angdata, PlotMarkers -> Graphics[{Black, PointSize[Large], Point[{0, 0}]}], Axes -> False, PolarTicks -> None, PolarAxes -> {True, False}, PolarGridLines -> False, PolarAxesOrigin -> {Pi, 10} ] ] CircularDotHistogram[data, 72, True] 

enter image description here

$\endgroup$
0
6
$\begingroup$
data = (RandomVariate[NormalDistribution[45, 20], 100] // Ceiling) ~ Join~(RandomVariate[NormalDistribution[245, 20], 100] // Ceiling); cpts0 = N@CirclePoints[{1, 90 Degree}, 360]; binsize = 5; rings = (binsize (Quotient[data, binsize]) // Sort[#] & // Split // Flatten[#, {{2}, {1}}] &) /. 0 -> 1; g = ReflectionMatrix[{1, 0}] . # &; f = {ColorData[{"Rainbow", {1, Length@rings}}][First@#2] , g /@ ((1 + 0.05 First@#2) cpts0[[#1]])} &; cpts2 = MapIndexed[f, rings]; Graphics[{ Circle[{0, 0}, 1] , AbsolutePointSize[6], Red , {#1, Point /@ #2} & @@@ cpts2 } ] 

circular plot of data frequency

$\endgroup$
4
$\begingroup$

Depending on the amount of data sometimes jittering the data can give one a sense of how the angles are distributed.

Borrowing heavily from @MrWizard 's answer...

data = {8, 9, 13, 13, 14, 18, 22, 27, 30, 34, 38, 38, 40, 44, 45, 47, 48, 48, 48, 48, 50, 53, 56, 57, 58, 58, 61, 63, 64, 64, 64, 65, 65, 68, 70, 73, 78, 78, 78, 83, 83, 88, 88, 88, 90, 92, 92, 93, 95, 96, 98, 100, 103, 106, 113, 118, 138, 153, 153, 155, 204, 215, 223, 226, 237, 238, 243, 244, 250, 251, 257, 268, 285, 319, 343, 350}; (* Jitter both the angle and distance from center of circle *) r = RandomVariate[UniformDistribution[{10.5, 11.5}], Length[data]]; \[Theta] = RandomVariate[UniformDistribution[{-0.5, 0.5}], Length[data]]; jitteredData = Transpose[{r Cos[2 \[Pi] (data + \[Theta])/360], r Sin[2 \[Pi] (data + \[Theta])/360]}]; (* Jittered plot *) labelfn = Text[HoldForm[# °], 8.25 {Sin[# °], Cos[# °]}] &; linefn = Rotate[Line[{{9.5, 0}, {10, 0}}], Pi/2 - # °, {0, 0}] &; linefn2 = Rotate[Line[{{9.1, 0}, {10, 0}}], Pi/2 - # °, {0, 0}] &; Graphics[{Circle[{0, 0}, 10], labelfn /@ Range[0, 330, 30], linefn /@ Range[0, 355, 5], linefn2 /@ Range[0, 330, 30], PointSize[0.02], Point[jitteredData]}] 

Jittered circular scatter plot of the angles

$\endgroup$
2
$\begingroup$

Note that the data mentioned by the OP is the turtle directional data found at various locations and specifically at https://purl.stanford.edu/rw590rq7988.

data = {8, 9, 13, 13, 14, 18, 22, 27, 30, 34, 38, 38, 40, 44, 45, 47, 48, 48, 48, 48, 50, 53, 56, 57, 58, 58, 61, 63, 64, 64, 64, 65, 65, 68, 70, 73, 78, 78, 78, 83, 83, 88, 88, 88, 90, 92, 92, 93, 95, 96, 98, 100, 103, 106, 113, 118, 138, 153, 153, 155, 204, 215, 223, 226, 237, 238, 243, 244, 250, 251, 257, 268, 285, 319, 343, 350}; 

In many studies there is the assumption (or hope) that there is an underlying smooth probability density function and we can use Mathematica's SmoothKernelDistribution to do so but for directional data there is a modification of the original data that needs to be performed.

Because the density at 0 and 360 is the same value (and the same for -20 and 340, and so on), we can account for that by joining 3 copies of the dataset but shifting by -360, 0, and 360 degrees, respectively, and then fit a nonparametric density estimate. See Silverman equation 2.17 on page 21 of https://ned.ipac.caltech.edu/level5/March02/Silverman/paper.pdf.

I'll also temporarily convert to radians which in this case seems to allow SmoothKernelDistribution to use the "LeastSquaresCrossValidation" option.

(* Augment the dataset *) data2 = Join[data - 360, data, data + 360] 2 π/360; (* Fit the distribution *) skd = SmoothKernelDistribution[data2, "LeastSquaresCrossValidation"]; (* Get a table of the probability density values appropriately scaled: the factor of 3 is used to multiply the pdf from `skd` because we concatenated 3 datasets. The multiplication by `2 π/360` is to convert from radians back to degrees. *) pdf = Table[3 PDF[skd, 2 π θ/360]*2 π/360, {θ, 0, 360}]; 

Now plot the density on a circular display with jittered data (again, borrowing heavily from @Mr.Wizard 's answer).

(* Jitter both the angle and distance from center of circle *) r = RandomVariate[UniformDistribution[{1.05, 1.15}], Length[data]]; θ = RandomVariate[UniformDistribution[{-0.5, 0.5}], Length[data]]; jitteredData = Transpose[{r Cos[2 π (data + θ)/360], r Sin[2 π (data + θ)/360]}]; s = 50; c = 1; pdfScaled = Table[{(c + s pdf[[i + 1]]) Cos[2 π i/360], (c + s pdf[[i + 1]]) Sin[2 π i/360]}, {i, 0, 360}]; labelfn = Text[HoldForm[# °], .825 {Sin[# °], Cos[# °]}] &; linefn = Rotate[Line[{{.95, 0}, {1, 0}}], Pi/2 - # °, {0, 0}] &; linefn2 = Rotate[Line[{{.91, 0}, {1, 0}}], Pi/2 - # °, {0, 0}] &; Show[ListPlot[pdfScaled, Joined -> True, AspectRatio -> 1, PlotRange -> {{-2, 2}, {-2, 2}}, Axes -> False, ImageSize -> Large], Graphics[{Circle[{0, 0}, 1], labelfn /@ Range[0, 330, 30], linefn /@ Range[0, 355, 5], linefn2 /@ Range[0, 330, 30], PointSize[0.01], Point[jitteredData]}]] 

Jittered data and pdf on a circular plot

In this case I think plotting the pdf on a linear scale is more informative or at least easier to interpret.

Show[ListPlot[pdf, Joined -> True, Frame -> True, PlotStyle -> PointSize[0.01], PlotRange -> {{0, 360}, {0, Automatic}}, FrameLabel -> (Style[#, Bold, 18] &) /@ {"Angle", "Probability density"}, PlotRangePadding -> {Automatic, {0, Automatic}}], ListPlot[Transpose[{data, 0.001 + 0.0005 (RandomReal[{0, 1}, Length[data]] - 0.5)}]]] 

Jittered data and pdf on a linear scale

$\endgroup$

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.