计算Haskell中Int列表的多个截止百分比?

时间:2017-10-16 20:20:55

标签: algorithm haskell statistics

我想针对以下问题编写一个Haskell函数:给出两个整数列表("深度"和"阈值"),返回平均深度和深度分数每个阈值大于或等于。 例如,给定深度[10,15,20]和阈值[13.333333,1.0,0.5,0.16666667],预期结果应为-- getMeanAndPercents (thresholds, depths): -- sum = 0 -- threshold_counts = [0 for t in thresholds] -- len = 0 -- for d in depths: -- sum += d -- len += 1 -- for t in thresholds: -- if d >= t: tresholds_counts[t] += 1 -- for t in tresholds -- threshold_percents[t] = thresholds_counts[t] / len -- return = [sum / len] + [threshold_percents] (平均深度为13.3,深度的100%> = 10,深度的50% > = 15& 17%的深度> = 20)。

命令式伪代码解决方案可能如下所示:

getMeanAndPercents :: [Int] -> [Int] -> [Float]
getMeanAndPercents thresholds depths = let
    depths2 = map f depths -- replace each depth d with [d, 1, 0, 1, 0, 1, 0, 0, ...]
    counts = foldl1 (zipWith (+)) depths2 -- sum the inner lists
    len = fromIntegral $ length depths -- get total depth length
                                       in fromIntegral (head counts) / len : map (\c -> fromIntegral c / len) (tail counts)
    where
        f d = d : map (\t -> if d >= t then 1 else 0) thresholds

我的Haskell解决方案如下所示:

let ... in

问题:

1)如何缩进此代码(我不确定如何将wheredepths混合)?

2)Haskell多久会循环length?我假定foldl1 & map一次,O(d*t)一次。

3)在Haskell中实现什么更好(w.r.t.性能和/或可读性)? (我假设至少需要做>=,即循环所有深度的所有阈值)

编辑:深度没有排序,阈值可以由函数排序。因此,一旦第一个阈值未通过测试,就可以跳过In [6]: df = pd.DataFrame(np.random.randint(100,size=(10,10))) \ .assign(Count=np.random.rand(10)) In [7]: df Out[7]: 0 1 2 3 4 5 6 7 8 9 Count 0 89 38 89 68 6 32 22 12 70 69 0.225977 1 49 1 38 33 36 93 9 57 93 64 0.030430 2 2 53 49 79 80 86 26 22 31 41 0.629740 3 38 44 23 29 75 42 75 19 99 57 0.980604 4 45 2 60 74 35 77 46 43 63 55 0.136395 5 65 97 15 16 88 59 23 68 5 21 0.648485 6 95 90 45 75 24 32 72 76 32 75 0.703680 7 60 31 65 85 4 36 52 72 73 94 0.744026 8 64 96 96 15 75 22 20 68 56 39 0.500358 9 78 54 74 29 87 57 33 97 63 37 0.289975

1 个答案:

答案 0 :(得分:1)

这段代码应该如何缩进?

这是主观的 - 我个人更喜欢这个:

getMeanAndPercents :: [Int] -> [Int] -> [Float]
getMeanAndPercents thresholds depths
  = let depths2 = map f depths
        counts  = foldl1 (zipWith (+)) depths2
        len     = fromIntegral $ length depths
    in fromIntegral (head counts) / len : map (\c -> fromIntegral c / len) (tail counts)
 where f d = d : map (\t -> if d >= t then 1 else 0) thresholds

Haskell多久会越过深度?我假设一次为长度,一次为foldl1&地图。

Haskell中的列表实际上不是列表,而是懒惰的数据流,就像python等命令式语言中的生成器一样。

因为计算取决于流的长度,所以我们必须经历一次以查看它的长度,这也会迫使您将整个列表加载到内存中。

您可以为阈值列表中的每个项目循环深度列表一次,然后再次获取总和,这是O(2d+dt)

如果您确实知道列表的长度,则应将其传递给函数,或者您可以使用其他数据类型,如Array,其长度已知。

在Haskell中实现什么更好(w.r.t.性能和/或可读性)? (我假设一个人需要做至少O(d * t),即循环所有深度的所有阈值)

我会写这样的实现:

getMeanAndPercents :: [Int] -> [Int] -> (Float, [Float])
getMeanAndPercents thresholds depths
  | len    <- fi $ length depths
  , (c:cs) <- foldl1 (zipWith (+)) $ f <$> depths
  = (fi c / len, (/len).fi <$> cs)
 where f d = d : map (\t -> if d >= t then 1 else 0) thresholds
       fi  = fromIntegral

这更接近您的命令性代码:

getMeanAndPercents thresholds depths
  | ((sum, len), ts) <- mapAccumL
      ( \(sum, len) d -> ( (sum+d, len+1)
                         , map (\t -> if d>=t then 1 else 0) thresholds
                         )
      ) (0,0) depths
  = (/len) <$> (sum:foldl1(zipWith(+))ts)

这更具可读性/意识形态haskell:

getMeanAndPercents :: [Int] -> [Int] -> (Float, [Float])
getMeanAndPercents thresholds depths
  | len <- length depths
  = ( sum depths / len
    , [ length (filter (>=t) depths) / len | t <- thresholds ]
    )