# random split a dataset

I've got a very simple problem, but not so trivial. I need to split a dataset (input and output data) in three part randomly: training set 70%, validation set 20% and is test set 10%. First of all this splitting have to be random, but reproducible, so I set SeedRandom. So I produce this simple code to split in two part (70% and 30%), but I've some problem to split in three part (70%, 20% and 10%). input is a matrix and oputput a list of numbers, for example:

input= {{0.688217, 0.362944, 0.468409, 0.133651}, {0.746246, 0.365313,
0.661147, 0.124928}, {0.688217, 0.362944, 0.892652,
0.133651}, {0.736027, 0.327411, 0.429482, 0.138367}, {0.765381,
0.390355, 0.81139, 0.148402}, {0.869656, 0.485533, 0.851523,
0.200265}, {0.757873, 0.412183, 0.573253, 0.174008}, {0.64025,
0.323266, 0.477443, 0.1111}, {0.648592, 0.327327, 0.614614,
0.115339}, {0.993743, 0.701184, 0.71341, 0.290435}, {0.763712,
0.542893, 0.685418, 0.22885}};

output={0.707695, 0.641002, 0.576708, 0.514697, 0.454514, 0.395702,
0.338031, 0.280931, 0.224174,0.1211,0.23234};

SeedRandom[1234];
index = RandomSample[Range[Length[input]], IntegerPart[Length[input]*0.3]];
indexp = Partition[index, 1];
inputv = {};
outputv = {};
(*Validation TEST*)
inputv = Table[Append[inputv, input[[i]]], {i, index}];
outputv = Table[Append[outputv, output[[i]]], {i, index}];
(*Test TEST*)
inputt = Delete[input, indexp];
outputt = Delete[output, indexp];

inputv = Flatten[inputv, 1];
outputv = Flatten[outputv];


Could you help me? Thanks...

-
The easiest way would be to shuffle the data randomly and then just take the first $70\%$, the next $20\%$, and the last $10\%$. (Edit: I have to run; anyone else please feel free to turn this into a proper answer with working code.) –  Rahul May 25 at 20:20
If my answer is not the right one for you, please explain why and I will update it, because I don't understand what the relationship between the output and input is supposed to be. –  Pickett May 25 at 20:39
I've got an input variable that is a set of four parameters, a matrix, and I want to simulate with a neural network one output. So I need to choose randomly my training, validation and test set. I need to choose randomly the same index of input and output and split this two database. –  Mary May 25 at 21:07
What about now, is it right? –  Pickett May 25 at 21:51
Are any of the answers what you need? If so, perhaps accept one (I note you seem to have never accepted any answer to any of your questions - are you aware of the option?). If none answer your question, perhaps further elucidate the op... –  rasher May 28 at 22:06

## 4 Answers

Other elegant implementation

rs = RandomSample[Range[Length[input]]];
train = rs[[1 ;; Round[Length[input]*0.7]]];
validation= rs[[Round[Length[input]*0.7] + 1 ;;Round[Length[input]*0.9]]];
test = rs[[Round[Length[input]*.9] + 1 ;;]];


and then use

input[[train]]
input[[validation]]
input[[test]]

-

Implementation of Rahul's suggestion in the comments:

SeedRandom[1234];
random = RandomSample[Transpose[{input, output}]];
threshold1 = Floor[0.7 Length[input]];
threshold2 = Floor[0.9 Length[input]];
training = random[[;; threshold1]];
validation = random[[threshold1 + 1 ;; threshold2]];
test = random[[threshold2 + 1 ;;]];

getInput[list_] := First /@ list;
getOutput[list_] := Last /@ list;


getInput[test] will give you the input of the test set, while getOutput[test] will get you the output.

-
You can also do {trainingInput, trainingOutput} = Transpose[training] and so on. –  Rahul May 25 at 23:31
{training, validation, test} =
Module[{in = #1, out = #2, l = Length@#},
(Transpose[{in, out}][[#]] & /@
Complement @@@ Partition[FoldList[Drop, RandomSample@Range@l,
Round[{l .7, l .2, l .1}]], 2, 1])] &[input, output]


training, validation, and test will be lists of the proportions desired, each consisting of the lists of input and corresponding output elements.

Here's an adaptation that's not limited to just two lists, so if in the future you need to do the same thing with more than two lists, problem solved:

Module[{lists = #1, l = Length@#[[1]]},
Transpose[lists][[#]] & /@ Complement @@@
Partition[FoldList[Drop, RandomSample@Range@l,
Round[l #2/Total@#2]], 2, 1]] &[{input, output}, {.7, .2, .1}]


So say you decide after the fact you wanted prepend the position that the new combined lists came from into the lists. No problem:

Module[{lists = #1, l = Length@#[[1]]},
Transpose[lists][[#]] & /@ Complement @@@
Partition[FoldList[Drop, RandomSample@Range@l,
Round[l #2/Total@#2]], 2, 1]] &[{Range@Length@input,input, output}, {.7, .2, .1}]

(*

{{{2, {0.746246, 0.365313, 0.661147, 0.124928}, 0.641002},
{4, {0.736027, 0.327411, 0.429482, 0.138367},0.514697},
{6, {0.869656, 0.485533, 0.851523, 0.200265},0.395702},
{7, {0.757873, 0.412183, 0.573253, 0.174008}, ...
{{1, {0.688217, 0.362944, 0.468409, 0.133651},0.707695}}}

*)


Note that the lists to be combined and the weights are provided as list arguments. Also, the weights are normalized, that is, you can put any numeric values that make sense and they'll work (you could use, e.g., {7,2,1} for your example). Whatever values you provide will be scaled to the correct percentage. This makes things pretty convenient: Say your lists had 135 elements, and you wanted four splits with 60, 40, 20, and 15 elements each. You can just use {60, 40, 20, 15} for the weights, no need to figure percentages... but you can use percentages/fractions/etc. if desired.

-
partsF = With[{shuffle = RandomSample[Range@Length@#],
t1 = Floor[#2 Length[#1]],
t2 = Floor[(#2 + #3) Length[#1]]},
Extract[#, List /@ Rest@ Extract[shuffle,
{{{}}, {;; t1}, {t1 + 1 ;; t2}, {t2 + 1 ;;}}]]] &;


(See this Q/A on the above form of Extract)

SeedRandom[1234];
{inptraining, inpvalidation, inptest} = partsF[input, .7, .2, .1]
(* {{{0.688217, 0.362944, 0.468409, 0.133651},
{0.757873, 0.412183, 0.573253, 0.174008},
{0.993743, 0.701184, 0.71341,  0.290435},
{0.869656, 0.485533, 0.851523, 0.200265},
{0.763712, 0.542893, 0.685418, 0.22885},
{0.648592, 0.327327, 0.614614, 0.115339},
{0.64025, 0.323266, 0.477443, 0.1111}},
{{0.765381, 0.390355, 0.81139, 0.148402},
{0.736027, 0.327411, 0.429482,0.138367}},
{{0.688217, 0.362944, 0.892652, 0.133651},
{0.746246, 0.365313, 0.661147, 0.124928}}}   *)

SeedRandom[1234];
{outptraining, outpvalidation, outptest} = partsF[output, .7, .2, .1]
(* {{0.224174, 0.454514, 0.1211, 0.23234, 0.641002, 0.514697, 0.338031},
{0.395702, 0.280931},
{0.576708, 0.707695}}  *)

-
-1 The SeedRandom[] argument should be 42 –  belisarius May 25 at 20:20
Need to SeedRandom with same value for each (input and output) before call to partsF, else the correspondence between them is lost... –  rasher May 26 at 2:51
Thank you @rasher; edited the post with the correction. –  kguler May 27 at 21:46