F # - заменить поплавок надписью на основе сравнения в процентах - PullRequest
0 голосов
/ 21 мая 2018

Мне нужно сопоставить значения в tmp с метками в tmpRplcd на основе сравнения с процентилями в prcntls , но Array.map2 ошибка строки, потому что массивы имеют разную длину.

module SOQN = 
   open System
   open MathNet.Numerics.Statistics
   let tmp = [| 13.0; 17.0; 23.0; 11.0; 11.0; 13.0; 31.0; 
                19.0; 47.0; 29.0; 29.0; 19.0; 43.0; 37.0 |]
   let tmpPrcntls = 
      tmp
      |> Array.sort
   let lbls = [| "p0"; "p1"; "p2"; "p3"; "p4";"p5" |]
   let prcntls = [| Statistics.Percentile(tmpPrcntls,0)   // 11.0
                    Statistics.Percentile(tmpPrcntls,20)  // 13.0
                    Statistics.Percentile(tmpPrcntls,40)  // 19.0
                    Statistics.Percentile(tmpPrcntls,60)  // 28.6
                    Statistics.Percentile(tmpPrcntls,80)  // 35.8
                    Statistics.Percentile(tmpPrcntls,100) // 47.0
                 |]
   let lkpTbl = Map(Array.zip prcntls lbls)
   let tmpRplcd:string[] = 
      tmp
      |> Array.map2 (fun x y -> if x <= y then lkpTbl.[y] else "") prcntls
   let main = 
      printfn ""
      printfn "Percentile Test"
      printfn ""
      printfn "tmpPrcntls: %A" tmpPrcntls
      printfn "prcntls:%A" prcntls
      printfn "tmpRplcd:%A" tmpRplcd
      0
   [<EntryPoint>]
   main
   |> ignore

 // Expected Result:
 // tmpRplcd = [| "p1"; "p2"; "p3"; "p0"; "p0"; "p1"; "p4"; 
 //               "p2"; "p5"; "p4"; "p4"; "p2"; "p5"; "p5" |]   

Куда я иду не так?

Ответы [ 2 ]

0 голосов
/ 21 мая 2018

Здесь следует подход к тому, что вы собираетесь делать с вашей F # -программой.

Я придумал реализацию вычисления процентиля из http://www.dummies.com/education/math/statistics/how-to-calculate-percentiles-in-statistics/, которое показано в статистике.Модуль ниже.

namespace FSharpBasics

module Statistics =
    let percentile p (array: float[]) =
        let threshold = (float p / 100.0) * float (array |> Array.length)
        let thresholdCeiling = int (System.Math.Ceiling threshold)
        let thresholdInteger = int (threshold)
        array
            |> Array.sort
            |> Array.skip (thresholdCeiling - 1)
            |> Array.truncate (if thresholdInteger = thresholdCeiling then 2 else 1)
            |> Array.average

module PercentileTest =
    open System

    let tmp = [| 13.0; 17.0; 23.0; 11.0; 11.0; 13.0; 31.0;
                 19.0; 47.0; 29.0; 29.0; 19.0; 43.0; 37.0 |]

    let lbls = 
        [| for n in 0..20..100 -> "p" + string (n / 20) |]

    let prcntls = 
        [| for n in 0..20..100 -> Statistics.percentile n tmp |]

    let tmpPrcntls = 
        tmp |> Array.sort

    let lkpTbl = 
        Array.zip prcntls lbls

    let tmpRplcd : string[] =
        tmp
        |> Array.map (fun x -> 
                lkpTbl 
                |> Array.filter (fun (prcntl, lbl) -> prcntl <= x)
                |> Array.last
                |> snd)

    [<EntryPoint>]
    let main argv =
        printfn ""
        printfn "Percentile Test"
        printfn ""
        printfn "tmp: %A" tmp
        printfn "tmpPrcntls: %A" tmpPrcntls
        printfn "prcntls: %A" prcntls
        printfn "tmpRplcd: %A" tmpRplcd
        System.Console.ReadKey() |> ignore
        0 // return an integer exit code
(*---- output ----

Percentile Test

tmp: [|13.0; 17.0; 23.0; 11.0; 11.0; 13.0; 31.0; 19.0; 47.0; 29.0; 29.0; 19.0; 43.0;
  37.0|]
tmpPrcntls: [|11.0; 11.0; 13.0; 13.0; 17.0; 19.0; 19.0; 23.0; 29.0; 29.0; 31.0; 37.0; 43.0;
  47.0|]
prcntls: [|11.0; 13.0; 19.0; 29.0; 37.0; 47.0|]
tmpRplcd: [|"p1"; "p1"; "p2"; "p0"; "p0"; "p1"; "p3"; "p2"; "p5"; "p3"; "p3"; "p2"; "p4";
 "p4"|]
---- ----*)
0 голосов
/ 21 мая 2018

Я думаю, что вы используете map2 неправильно - функция map2 архивирует два массива, а затем применяет данную функцию к сжатому массиву.

Исходя из вашего вопроса, я предполагаю, что вына самом деле хочу сделать что-то еще.Для каждого ввода вы хотите перебрать все процентили и найти первый процентиль так, чтобы значение было больше (или меньше?), Чем процентиль.Для этого вам нужно заменить map2 на что-то вроде этого:

let tmpRplcd:string[] = 
  tmp 
  |> Array.map (fun y -> 
    prcntls |> Array.tryPick (fun x ->
      if x <= y then Some(lkpTbl.[x]) else None))
  |> Array.map (fun v -> defaultArg v "")

У меня нет подходящей версии, чтобы попробовать это, но я думаю, что это должно делать то, что вам нужно (япросто не уверен, если вам нужно x <= y или наоборот!)

...