3
$\begingroup$

I have a dataset (converted to a list of lists) that has 50 records, the first column of which represents the number individuals of a species taken in a trawl, the second column the minimum depth of that trawl in m, and the third column the maximum depth of the trawl in m.

The data are as follows:

md1 = {{1, 165, 165}, {2, 274, 274}, {1, 248, 268}, {1, 219, 219}, {3, 219, 
 232}, {12, 249, 256}, {11, 274, 274}, {1, 274, 274}, {14, 175, 
 183}, {2, 274, 274}, {2, 241, 247}, {1, 219, 232}, {1, 204, 
 204}, {1, 183, 183}, {1, 219, 256}, {5, 113, 115}, {5, 185, 
 185}, {7, 16.5, 16.5}, {1, 91.4, 91.4}, {1, 122, 122}, {1, 91.4, 
 91.4}, {2, 115, 121}, {9, 93.9, 96.9}, {2, 93.9, 93.9}, {9, 110, 
 119}, {35, 174, 174}, {3, 166, 236}, {7, 170, 192}, {5, 91.4, 
 91.4}, {1, 85.3, 85.3}, {9, 238, 238}, {2, 125, 130}, {9, 77, 
 79}, {4, 95, 136}, {2, 90, 128}, {1, 95, 136}, {2, 205, 210}, {2, 
 205, 210}, {1, 55, 91}, {1, 252, 260}, {1, 247, 247}, {1, 110, 
 110}, {2, 283, 283}, {8, 160, 160}, {5, 120, 120}, {1, 16, 37}, {25,
 146, 146}, {7, 139, 139}, {16, 55, 55}, {423, 65.8, 71.3}}

I can create a DistributionChart that approaches what I want as follows:

 mdepthprofile = 
 DistributionChart[{md1[[All, 2]], md1[[All, 3]]}, 
 ChartElementFunction -> "HistogramDensity", BarOrigin -> Top, 
 FrameLabel -> {Style["Pontinus n. sp.", Bold, Italic], 
 Style["Depth (m)", Bold]}]

However, this provides two distribution charts derived from the frequencies based on record counts from each of minimumdepth and maximumdepth columns and not the number of specimens that might be expected within a given bin assuming a uniform distribution of the specimens across the entire depth interval sampled by the trawl.

What I want instead is to create a DistributionChart with a ChartElementFunction -> "HistogramDensity", with the vertical axis the depth as above, but with the bin-sizes in 50 m intervals and with bin-count indicating how many total fish in column 1 were collected within each 50 m interval. For those trawls that have a depth range that spans a 50 m boundary, I want to divide the number of specimens (column 1) so that the frequency value represents the proportion the specimens collected in each bin, assuming a uniform distribution within the trawl. That is, if a record has a depth interval of 35-125 and with 25 specimens collected in that trawl, the 25 specimens would be split among 3 bins (0-50,51-100,100-150), so that the proportion in the first bin (0-50) being 25*(15/90), the second bin being 25*(50/90) and the third bin being 25*(25/90) (ie assuming a uniform distribution within each depth range (maxdepth-mindepth) for each record. Thus, the width of each bin in the final chart would reflect the subtotal of specimens in each bin accumulated over all trawls, with the subtotals adding to the total number of specimens collected.

How can one set up ChartDistribution so that the bin-sizes and frequencies (bar widths) are computed as described above?

$\endgroup$

2 Answers 2

3
$\begingroup$

This is maybe a bit torturous:

(* Set a binwidth *)
binwidth = 50;

(* Define a function that apportions the counts to bins *)
f[{count_, min_, max_}, binwidth_] := Module[{lowerBin, upperBin, result},
  lowerBin = Floor[min/binwidth];
  upperBin = Floor[max/binwidth];
  If[lowerBin == upperBin, result = {{count, lowerBin}},
   n = upperBin - lowerBin + 1; (* Number of bins involved *)
   (* Apportion counts *)
   result = ConstantArray[{0, 0}, n]; 
   result[[1]] = {count (binwidth*(lowerBin + 1) - min)/(max - min), lowerBin};
   result[[n]] = {count (max - binwidth*upperBin)/(max - min), upperBin};
   If[n > 2, 
    Do[result[[i]] = {count*binwidth/(max - min), lowerBin + i}, {i, 3, n - 1}]];
   result]]
counts = Flatten[f[#, binwidth] & /@ md1, 1];

(* Gather adjusted counts by bin midpoint and convert to weighted data *)
frequencies = Table[{Total[Select[counts, #[[2]] == i &][[All, 1]]], 
    binwidth*i + binwidth/2}, {i, Min[counts[[All, 2]]], Max[counts[[All, 2]]]}];
frequencies = WeightedData[frequencies[[All, 2]], frequencies[[All, 1]]];

(* Construct distribution chart *)
mdepthprofile = DistributionChart[frequencies, 
  ChartElementFunction -> "HistogramDensity", BarOrigin -> Top, 
  FrameLabel -> {Style["Pontinus n. sp.", Bold, Italic], 
  Style["Depth (m)", Bold]}]

Distribution chart with binwidth=50

If binwidth = 20, then the following results:

Distribution chart with binwidth=20

$\endgroup$
1
  • $\begingroup$ A little round about, but most logical. Thanks. $\endgroup$ Mar 18, 2022 at 20:32
2
$\begingroup$

An alternative way to apportion counts to bins and to construct a WeightedData object:

ClearAll[binLims, binInequalities, apportionCounts, toWeightedData]

binLims[bw_][data_] := Range[##, bw] & @@ Round[MinMax[Rest /@ data], bw]

binInequalities[binlims_][x_] := BlockMap[First[#] <= x < Last[#] &, binlims, 2, 1]

apportionCounts[binlims_][count_, min_, max_] := 
 Module[{ineqs = binInequalities[binlims][$x], 
   h = If[min == max, With[{$x = min}, Boole @ #] &, 
     Probability[#, $x \[Distributed] UniformDistribution[{min, max}]] &]},
  count  h /@ ineqs]

toWeightedData[bw_][data_] := Module[{bl = binLims[bw] @ data}, 
  WeightedData[MovingAverage[bl, 2], Total[apportionCounts[bl] @@@ data]]]

DistributionChart[toWeightedData[50] @ md1, 
 ChartElementFunction -> "HistogramDensity", BarOrigin -> Top,
 FrameLabel -> {Style["Pontinus n. sp.", Bold, Italic], Style["Depth (m)", Bold]}]

enter image description here

Use toWeightedData[20] @ md1 to get

enter image description here

$\endgroup$
1
  • $\begingroup$ In JimB's code above he also generates a WeightedData object, but this changes the bin size. Together both responses are instructive as to how bin size and chart width are computed. $\endgroup$ Mar 18, 2022 at 20:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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