0
$\begingroup$

I have a long list consists of float numbers with some of them duplicated as shown below.

list output

I want to divide this list into bins;

  1. with equal bin width so that in each bin there might be one or more than one member, even duplicated numbers come from the main list.
  2. with variable bin width so that in each bin there will be the same amount of members.

For the first item, I start with deciding on a binning amount and find suitable bin width (in this case 0.001) for this binning amount as below;

binningamount = 996;
min = Floor[Min[DeleteCases[Sort[DeleteDuplicates[list]], "NA"]], 0.1]
max = Ceiling[Max[DeleteCases[Sort[DeleteDuplicates[list]], "NA"]]]
Length[DeleteCases[BinLists[list, {min, max, 0.001}], {}]] == binningamount

and create binned list as below;

binning = 
  Table[Catch[
    Do[If[IntervalMemberQ[Interval[i], list[[j]]] == True, Throw[i]],
     {i, Partition[Range[min, max, 0.001], 2, 1]}]], {j, Length[list]}];

Output: output for binned list

I am wondering,

  • Is there a way to perform the first item easier than I showed above? For instance, giving the binning amount and get suitable bin width value to apply to list.
  • How is it possible to achieve the second item?
$\endgroup$
7
  • $\begingroup$ Have a look at Partition. $\endgroup$ Nov 5 '20 at 16:19
  • $\begingroup$ @b.gates.you.know.what, I suppose my question was too simple so I modified it with the real problems I have. $\endgroup$
    – kosif
    Nov 5 '20 at 17:28
  • $\begingroup$ Your question 1 is nonsensical, because in some bins there might be no element that would fit into that bin. Your question 2 is nonsensical too, because if, for example, the length of list was 7 then you can not divide it to, say, 3 bins of equal length because 3 does not divide 7. $\endgroup$ Nov 6 '20 at 17:09
  • $\begingroup$ Look at FindClusters - that is perhaps what you need instead of BinLists. $\endgroup$ Nov 6 '20 at 17:17
  • $\begingroup$ I guess FindCluster is not usable right now. My questions might require tailor-made solutions however they are not nonsensical. I had a satisfactory solution by my edited answer for the first question. For the second one, I will spend some time to have some meaningful outputs. $\endgroup$
    – kosif
    Nov 6 '20 at 18:36
1
$\begingroup$

I think the best way would be to use HistogramList:

HistogramList—Wolfram Language Reference

various sorts of bin specifications are supported.

$\endgroup$
1
  • $\begingroup$ thanks for this. Yes, HistogramList works quite efficiently to achieve the first item in my question even more practical than my solution. However, it does not have an option to force the data to obtain variable bin widths as I asked in the second item of my question. $\endgroup$
    – kosif
    Apr 25 at 22:28
0
$\begingroup$

binamount is equal:

binamount == Floor[(max - min)/binwidth]

Therefore binwidth lies in this interval:

(max - min)/(binamount + 1) < binwidth <= (max - min)/binamount

So for your original list:

list = {1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 8, 8, 9, 10, 10, 11, 12, 12, 12,
    12, 12, 12, 12, 13, 14, 15, 16};
{min, max} = MinMax[list];

If you want, for example, binamount==7 you can choose binwidth value from the following interval:

(max - min)/(binamount + 1) < binwidth <= (max - min)/binamount /. 
  binamount -> 7 // N
(*1.875 < binwidth <= 2.14286*)
$\endgroup$
5
  • $\begingroup$ thank you for the answer. As I understood, there is no quick way or as built function to find the suitable bin width interval for any requested binning amount. $\endgroup$
    – kosif
    Nov 6 '20 at 1:17
  • $\begingroup$ ??? What else do you need? I provided you with exact and quick way how to do it. $\endgroup$ Nov 6 '20 at 9:17
  • $\begingroup$ Your answer is not what I am looking for actually. If you could check my question (edited long time ago), I am applying a similar approach to yours. Your answer is for the first version (before edit) of my question which was not detailed enough. I mean it is a manual selection of some bin width value from an interval and applying it to get requested binning amount. I see that there is no straight-forward way to give binning amount as input and get possible bin width/ bin widths as an output. $\endgroup$
    – kosif
    Nov 6 '20 at 9:24
  • $\begingroup$ It is answer to your first question of the current version of your post. $\endgroup$ Nov 6 '20 at 9:33
  • $\begingroup$ I applied your answer however it is not working for all lists and with float numbers. I posted it as an answer to my own question. $\endgroup$
    – kosif
    Nov 6 '20 at 12:30
0
$\begingroup$

Regarding the first goal item, modifying the answer belongs to @azerbajdzan, I came up with a function that generates binning with the arguments; list itself and requested bin amount.

    binner[list_, binamount_] := 
 Module[{min, max, maxwidth, binnedlist, binamountcheck, result},
  min = Min[list];
  max = Max[list] + 1;
  maxwidth = (max - min)/binamount;
  binnedlist = BinLists[list, {min, max, maxwidth}];
  binamountcheck = Length[DeleteCases[binnedlist, {}]];
  result = 
   If[binamountcheck == binamount, binnedlist, 
    DeleteCases[binnedlist, {}] -> 
     Row[{Length[DeleteCases[binnedlist, {}]], 
       " bin(s) is/are created!"}]]]

This one works quite nice and gives list as binned in requested amount for suitable lists such as my sample list in the question.

>>> binner[list, 4]
Output: {{1, 2, 2, 3, 4}, {5, 5, 6, 7, 8, 8, 8}, {9, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12}, {13, 14, 15, 16}}

If some of the bins wouldn't have any elements (which means decrease in requested bin amount), function brings cleaned resultant binned list with a string information about the current bin amount. This string info would be useful for long lists binning trials as in my case.

    >>> list2 = {0.1, 0.2, 0.2, 0.3, 0.4, 0.2, 0.4, 0.5, 0.6, 0.4, 0.5, 0.6, 
       0.6, 0.6, 0.7, 0.8, 0.9, 1, 1.1, 1.6, 1.6, 1.7, 1.8, 1.9, 2, 2.3, 2.6};
    >>> binner[list2, 7]
    Output: {{0.1, 0.2, 0.2, 0.3, 0.4, 0.2, 0.4, 0.5, 0.4, 0.5}, {0.6, 0.6, 0.6, 
   0.6, 0.7, 0.8, 0.9, 1}, {1.1}, {1.6, 1.6, 1.7, 1.8, 1.9, 
   2}, {2.3}, {2.6}} -> 6 bin(s) is/are created!
$\endgroup$
4
  • $\begingroup$ What is the problem? It works as I said, both outputs are of length 7 as required. $\endgroup$ Nov 6 '20 at 13:50
  • $\begingroup$ In the second list, last bin is empty. $\endgroup$
    – kosif
    Nov 6 '20 at 14:17
  • $\begingroup$ Yes, it is empty. What else should it be when there is no element in the list2 that fit into that bin? BinLists is doing correctly, but you have nonsensical requirements. Empty list is like any other list, it is just that its length is 0. By the way it is first time you mention word "empty" - there is no word in your OP that would tell anything about excluding empty lists. $\endgroup$ Nov 6 '20 at 17:01
  • $\begingroup$ @azerbajdzan, yes I realised that was not Mathematica problem and I do not think my requirement is nonsensical. My intention is just to have a same binning result with my initial requested bin amount. If it's not then the function can give a warning as output which I have already added in my modified answer. $\endgroup$
    – kosif
    Nov 6 '20 at 18:29

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.