Lets say I have 101 instances of data with multiple attributes. In particular case, one attribute has 7 unique values. I want to get 7<=n<=101 element positions from data, in a way that it contains at least 1 of each unique attribute case.

First I get the example data and gather it by particular attribute:

data = Import["https://raw.github.com/BioGeek/aima/master/aima-data-read-only/zoo.csv"];
g = GatherBy[data, #[[18]] &];

List g of lists with element sizes h

h = Length[#] & /@ g (*{41,13,20,10,8,4,5}*)

Following works in the best case scenario

n = RandomInteger[{Length[g], Length[data]}]
l = RandomSample[RandomSample[#] & /@ IntegerPartitions[n, {Length[g]}], 1][[1]]
s = Table[RandomSample[g[[i]], l[[i]]], {i, 1, Length[g]}]
p = Sort[Position[data, #][[1]][[1]] & /@ #] & /@ s

Example result:


I can bruteforce, but I do not like the idea.

While[MemberQ[Table[h[[i]] >= l[[i]], {i, 1, Length[h]}], False], 
 l = RandomSample[RandomSample[#] & /@ IntegerPartitions[n, {Length[g]}], 1][[1]]]

How do I find random sample l so it is less then gap sizes h?


If n = 7

enter image description here

If n = 21

enter image description here

Problem is that h = {41,13,20,10,8,4,5} and even if n = 11 then l = {1,1,1,1,1,5,1} that is invalid as there are not enough elements in that sublist.

edit 2

f[data_List, n_Integer] := Module[{data1 = data, h, k},
  k = RandomSample[DeleteCases[data1, Alternatives @@
      (h = RandomChoice /@ GatherBy[data1, #[[18]] &])], n - 7];
  • 1
    $\begingroup$ Sorry, I'm having trouble understanding this. Could you try again to explain, please? $\endgroup$
    – Mr.Wizard
    Jan 2, 2013 at 15:21
  • $\begingroup$ If 'n = 7', i want to take sample positions of 7 unique elements from the data. If n is larger, i want to take more elements. Selection must have 1 element of each attribute 18 category {mammal, fish, bird, ...}(7 in total). $\endgroup$
    – Margus
    Jan 2, 2013 at 15:59
  • $\begingroup$ I can't understand your sentence "How do I find random sample l so it is less then gap sizes h?" $\endgroup$ Jan 2, 2013 at 16:20
  • $\begingroup$ Why not base the number of elements chosen in a given sublist on the length of that sublist? That way you will never request more than you have. [Note: You really have not stated clearly what is your "randomness" requirement. Thus it is difficult to give a specific answer.] $\endgroup$ Jan 2, 2013 at 16:36

1 Answer 1


This first chooses one element from each subset, and then takes the rest from the complement using the whole set (discarding those previously taken). It doesn't assume a fixed number of classes, and calculates it from the length of the first choice.

f[data_List, n_Integer] := 
   #~Union~RandomSample[DeleteCases[data, Alternatives @@ #], n - Length@#] &@
                                              (RandomChoice /@ GatherBy[data, #[[18]] &])

Your Answer

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

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