I need to map the values in tmp to labels in tmpRplcd based on comparison with percentiles in prcntls, but the Array.map2 line fails because the arrays are of different lengths.
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" |]
Where am I going wrong?
I think your use of map2 is wrong - the map2 function zips the two arrays and then applies the given function to the zipped array.
Based on your question, my guess is that you actually want to do something else. For every input, you want to iterate over all percentiles and find the first percentile such that the value is greater (or smaller?) than the percentile. To do this, you need to replace map2 with something like this:
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 "")
I don't have the right version to try this, but I think this should do what you need (I'm just not sure if you need x <= y or the other way round!)
Here follows an approach to what you'd intend to do with your F# program.
I came up with an implementation of the percentile calculation from http://www.dummies.com/education/math/statistics/how-to-calculate-percentiles-in-statistics/ , which is shown in the Statistics module below.
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"|]
---- ----*)
Related
I have three arrays - first is a float array, second is a string array, and fltr is a string array. I need to generate a product of the elements in the first array filtered by whether the matching index in the second array contains all the characters in the elements of the filter array:
module SOQN =
open System
let first = [| 2.00; 3.00; 5.00; 7.00; 11.00 |]
let second = [| "ABCD"; "ABCE"; "ABDE"; "ACDE"; "BCDE" |]
let fltr = [| "AC"; "BD"; "CE" |]
let result =
first
|> Array.filter second // filter for elements containing characters in second array
|> Seq.reduce (fun x y -> x * y)
// Expected Result: let result = [| 42.00; 110.00; 231.00 |]
How do I generate the array of products?
Something like this
let first = [| 2.00; 3.00; 5.00; 7.00; 11.00 |]
let second = [| "ABCD"; "ABCE"; "ABDE"; "ACDE"; "BCDE" |]
let fltr = "AC"
Array.zip first second
|> Array.filter (fun (_, s) ->
Seq.forall (fun c -> s.Contains (string c)) fltr)
|> Array.map fst
|> Array.reduce (*)
The following snippet (though not idiomatic) provides the complete answer I was seeking and includes #xuanduc987 solution:
module SOANS =
open System
let first = [| 2.00; 3.00; 5.00; 7.00; 11.00 |]
let second = [| "ABCD"; "ABCE"; "ABDE"; "ACDE"; "BCDE" |]
let fltr = [| "AC"; "BD"; "CE" |]
let filterProduct (first:float[]) (second:string[]) (fltr:string) =
Array.zip first second
|> Array.filter (fun (_, s) ->
Seq.forall (fun c -> s.Contains (string c)) fltr)
|> Array.map fst
|> Array.reduce (*)
let third =
[for i in [0..fltr.Length - 1] do
yield (filterProduct first second fltr.[i])]
|> List.toArray
printfn "Third: %A" third
// Expected Result: Third: [| 42.0; 110.0; 231.0 |]
// Actual Result Third: [| 42.0; 110.0; 231.0 |]
I am trying to apply a function to two arrays of different lengths but the result is not matching my expectations. I need to reuse elements of the second array (similar to R) to complete the expression:
module SOQN =
open System
let first = [|2; 3; 5; 7; 11|]
let second = [|13; 17; 19|]
let result =
[for i in [0..first.Length-1] do
for j in [0..second.Length-1] do
yield (first.[i] * second.[j])]
printfn "Result: %A" result
// Actual Result: [|26; 34; 38; 39; 51; 57; 65; 85; 95; 91; 119; 133; 143; 187; 209|]
// Expected Result: [|26; 51; 95; 91; 187|]
What am I missing?
I guess you looking for something like this
let result =
Array.mapi
(fun i v -> second.[i % second.Length] * v) first
Here is another way of doing it:
let first = [|2; 3; 5; 7; 11|]
let second = [|13; 17; 19|]
let map2Cycle (x: 'T []) (y: 'T []) (f: 'T -> 'T -> 'S) =
let lenx = Array.length x
let leny = Array.length y
let xInf = Seq.initInfinite (fun _ -> x) |> Seq.concat
let yInf = Seq.initInfinite (fun _ -> y) |> Seq.concat
Seq.map2 f xInf yInf
|> Seq.take (max lenx leny)
|> Array.ofSeq
map2Cycle first second (*)
// val it : int [] = [|26; 51; 95; 91; 187|]
And here is a slightly different one:
let map2Cycle' (x: 'T []) (y: 'T []) (f: 'T -> 'T -> 'S) =
let lenx = Array.length x
let leny = Array.length y
let (x', y') =
if lenx >= leny then x |> Seq.ofArray, Seq.initInfinite (fun _ -> y) |> Seq.concat
else Seq.initInfinite (fun _ -> x) |> Seq.concat, y |> Seq.ofArray
Seq.map2 f x' y'
|> Seq.take (max lenx leny)
|> Array.ofSeq
map2Cycle' first second (*)
// val it : int [] = [|26; 51; 95; 91; 187|]
I have a program that results in an [] list, and I'm trying to remove near duplicated arrays from the list. An example of the list is...
[
[|
"Jackson";
"Stentzke";
"22";
"001"
|];
[|
"Jackson";
"Stentzke";
"22";
"002"
|];
[|
"Alec";
"Stentzke";
"18";
"003"
|]
]
Basically I'm trying to write a function that would read over the list and remove all examples of near identical data. So the final returned [] list should look like...
[
[|
"Alec";
"Stentzke";
"18";
"003"
|]
]
I've tried a number of functions to try and get this result or something close to it that can work with. My current attempt is this...
let removeDuplicates (arrayList: string[]list) =
let list = arrayList|> List.map(fun aL ->
let a = arrayList|> List.map(fun aL2 ->
try
match (aL.GetValue(0).Equals(aL2.GetValue(0))) && (aL.GetValue(2).Equals(aL2.GetValue(2))) && (aL.GetValue(3).Equals(aL2.GetValue(3))) with
| false -> aL2
| _ -> [|""|]
with
| ex -> [|""|]
)
a
)
list |> List.concat |> List.distinct
But all this returns is the a reversed version on the input []list.
Does anyone know how to remove near duplicated arrays from a list?
I believe your code and comments don't match up very well. Considering your comments "the first, second and third values are the same", I believe this can get you in the right track:
let removeDuplicates (arrayList: string[]list) =
arrayList |> Seq.distinctBy (fun elem -> (elem.[0] , elem.[1] , elem.[2]))
The result of this against your input data is a two element list containing:
[
[|
"Jackson";
"Stentzke";
"22";
"001"
|];
[|
"Alec";
"Stentzke";
"18";
"003"
|]
]
You should create a dictionary/map based on the fields you consider identical then just remove any duplicate occurance. Here's a simply and mechanical way, assuming xs is the List you specified above:
type DataRec = { key:string
fname:string
lname:string
id1:string
id2:string}
let dataRecs = xs |> List.map (fun x -> {key=x.[0]+x.[1]+x.[2];fname=x.[0];lname=x.[1];id1=x.[2];id2=x.[3]})
dataRecs |> Seq.groupBy (fun x -> x.key)
|> Seq.filter (fun x -> Seq.length (snd x) = 1)
|> Seq.collect snd
|> Seq.map (fun x -> [|x.fname;x.lname;x.id1;x.id2|])
|> Seq.toList
Output:
val it : string [] list = [[|"Alec"; "Stentzke"; "18"; "003"|]]
It basically creates a key from the first three items, groups by it, filters out anything over 2 counst, and then maps back to an array.
Using some Linq:
let comparer (atMost) =
{ new System.Collections.Generic.IEqualityComparer<string[]> with
member __.Equals(a, b) =
Seq.zip a b
|> Seq.sumBy (fun (a',b') -> System.StringComparer.InvariantCulture.Compare(a', b') |> abs |> min 1)
|> ((>=) atMost)
member __.GetHashCode(a) = 1
}
System.Linq.Enumerable.GroupBy(data, id, comparer 1)
|> Seq.choose (fun g -> match Seq.length g with | 1 -> Some g.Key | _ -> None)
The comparer allows for atMost : int number of differences between two arrays.
Let's say that I want to do some in-array comparison in F#, e.g. determine whether a given integer array arr contains integers in strictly increasing order. I would do it like this:
let arri =
arr
|> Array.mapi (fun i e -> i, e)
let isArrStrictlyIncreasing =
arri
|> Array.tryFind (fun e ->
if fst e = arri.Length - 1 then
false
else
snd e >= snd arri.[1 + fst e])
|> Option.isNone
However, I am wondering if there is a more concise, "functional" way of making such in-array comparisons without the use of Array.mapi and if-else?
One simple way:
let isincr arr =
arr
|> Array.fold (fun (a,b) (c) -> ((b<c && a)),c) (true,System.Int32.MinValue)
|> fst
Couldn't you just do the following?
let expected = [| candidate.[0] .. candidate.[0] + candidate.Length - 1 |]
candidate = expected
This simply builds the array you'd expect, and uses F#'s Structural Equality to compare the two arrays.
A more general alternative might be this:
let isOrdered arr = arr |> Array.sort = arr
let hasDistinctMembers (arr : 'a array) =
let distinct = arr |> Seq.distinct |> Seq.toArray
distinct.Length = arr.Length
let isStrictlyIncreasing arr = arr |> isOrdered && arr |> hasDistinctMembers
Usage:
> isStrictlyIncreasing [| 7; 11; 8 |];;
val it : bool = false
> isStrictlyIncreasing [| 7..33 |];;
val it : bool = true
> isStrictlyIncreasing [| 7; 8; 11 |];;
val it : bool = true
> isStrictlyIncreasing [| 7; 7; 8; 11 |];;
val it : bool = false
Different data strucs are good/better for some things not so much for others.
Is there a way to convert a Vector array to be used by WPF Polygon?
let mutable nodes = [| Vector (100.0, 100.0); Vector (60.0, 20.0); Vector (80.0, 60.0); Vector(50.0, 100.0); Vector (100.0, 80.0); Vector (30.0, 140.0); Vector (20.0, 80.0); Vector (30.0, 30.0); |]
let spolyS : Point seq = Seq.ofArray [| Vector (0.0, 0.0); Vector (6.0, 2.0); Vector (8.0, 6.0); Vector(5.0, 10.0); Vector (10.0, 8.0); Vector (3.0, 14.0); Vector (-2.0, 8.0); Vector (-3.0, 3.0); |]
let seqFromArray1 = [| 1..10 |] :> seq<int>
let seqFromArray2 = [| 1 .. 10 |] |> Seq.ofArray
let seqFromArray3 = polyS :> seq<Point>
let mutable arrayList1 = new System.Collections.ArrayList(10)
for i in 1 .. 10 do arrayList1.Add(10) |> ignore
let seqCast : seq<int> = Seq.cast arrayList1
let mutable arrayList2 = new Media.PointCollection(10)
for i in 1 .. 10 do arrayList1.Add(10) |> ignore
let seqCast : seq<Point> = Seq.cast arrayList2
let spts : Point seq = Seq.ofArray polyS |> new Media.PointCollection
let spts : Point seq = Seq.ofArray polyS |> new Media.PointCollection
let spts : Point seq = Seq.ofArray polyS |> new Media.PointCollection(spts IEnumerable<Point>):unit
Maybe something like:
let vectors = [| Vector(100.0, 100.0); Vector(60.0, 20.0); ... |]
let points = vectors |> Array.map (fun v -> Point(v.X, v.Y))
let mediaPoints = Media.PointCollection(points)