2012-07-21 117 views
3

我有一个集合,我想按任意百分比分割。我试图解决的实际问题是将数据集分为训练集和交叉验证集。我如何将一个集合分成两部分,按百分比给出

应该随机选择每个元素的目的地,但每个源元素只应在结果中出现一次,并且分区的大小是固定的。如果源集合具有重复项,则重复项可能出现在不同的输出分区中或相同。

我有这样实现:

(defn split-shuffled 
    "Returns a 2 element vector partitioned by the percentage 
    specified by p. Elements are selected at random. Each 
    element of the source collection will appear only once in 
    the result." 
    [c p] 
    (let [m (count c) 
     idxs (into #{} (take (* m p) (shuffle (range m)))) 
     afn (fn [i x] (if (idxs i) x)) 
     bfn (fn [i x] (if-not (idxs i) x))] 
    [(keep-indexed afn c) (keep-indexed bfn c)])) 

repl> (split-shuffled (range 10) 0.2) 
[(4 6) (0 1 2 3 5 7 8 9)] 

repl> (split-shuffled (range 10) 0.4) 
[(1 4 6 7) [0 2 3 5 8 9)] 

但我不开心keep-indexed被调用了两次。

这怎么能改进?

编辑:我原本想保持在分区的顺序,但我没有重新思考而放弃了这个要求,所以@迈克拉的解决方案是正确的!

回答

5

为什么你需要索引呢?

就直接洗牌集合:

(defn split-shuffled 
    [c p] 
    (let [c (shuffle c) 
      m (count c) 
      t (* m p)] 
     [(take t c) (drop t c)])) 
+1

我本来想维持秩序,两个分区,但我放弃了这一要求,所以你的答案是正确的,我已经变得更困难,我需要。 – sw1nn 2012-07-21 17:00:39

+2

通过使用split-by可以更简短。 (split * at(split * at(* p(count c)) (shuffle c))) – sortega 2012-07-23 21:45:06