Arka plan: Bir dizi bitişik, zaman damgalı veriye sahibim. Veri dizisinde, içinde delikler, bazıları büyük, diğerleri sadece tek bir eksik değer var.
Delik sadece tek bir eksik değer olduğunda, delikleri bir taklit değeriyle yamalamak istiyorum (daha büyük delikler dikkate alınmayacaktır).Bu örnekte neden bir diziyi kullanmaktan çok daha yavaş kullanıyorsunuz
Yamalı dizinin tembel neslini kullanmak istiyorum ve bu nedenle Seq.unfold kullanıyorum.
Verilerin deliklerini yamalamak için iki yöntem sürümü yaptım.
ilk olarak içinde delikler olan veri dizisini tüketir ve yamalı sekansı üretir. İstediğim şey buydu, ama giriş dizisindeki elemanların sayısı 1000'in üzerine çıktığında, yöntemler korkunç bir şekilde yavaşlar ve giriş dizisinin içerdiği daha fazla elementi gittikçe daha da kötüleştirir.
ikinci yöntem, delikli bir veri listesini tüketir ve yamalı sekansı üretir ve hızlı çalışır. Ancak bu, istediğim şey değil, çünkü bu, tüm giriş listesinin belleğe alınmasını zorlar.
Tüm giriş listesini aynı anda bellekte bulundurmaktan kaçınmak için (liste -> sıra) yöntemi yerine (dizi -> dizi) yöntemini kullanmak istiyorum.
Sorular:
1) Neden ilk yöntem (Ben, bu art arda Seq.skip 1 ile yeni dizileri oluştururken ile ilgisi var şüphelenen) büyük girdi listeleri ile giderek kötüleşiyor (çok yavaş ama Ben bir giriş kullanırken dizisi ziyade bir giriş listeye daha
2) Nasıl hızlı veri deliklerin yama yapabilirsiniz) emin değilim?
kodu:
open System
// Method 1 (Slow)
let insertDummyValuesWhereASingleValueIsMissing1 (timeBetweenContiguousValues : TimeSpan) (values : seq<(DateTime * float)>) =
let sizeOfHolesToPatch = timeBetweenContiguousValues.Add timeBetweenContiguousValues // Only insert dummy-values when the gap is twice the normal
(None, values) |> Seq.unfold (fun (prevValue, restOfValues) ->
if restOfValues |> Seq.isEmpty then
None // Reached the end of the input seq
else
let currentValue = Seq.hd restOfValues
if prevValue.IsNone then
Some(currentValue, (Some(currentValue), Seq.skip 1 restOfValues )) // Only happens to the first item in the seq
else
let currentTime = fst currentValue
let prevTime = fst prevValue.Value
let timeDiffBetweenPrevAndCurrentValue = currentTime.Subtract(prevTime)
if timeDiffBetweenPrevAndCurrentValue = sizeOfHolesToPatch then
let dummyValue = (prevTime.Add timeBetweenContiguousValues, 42.0) // 42 is chosen here for obvious reasons, making this comment superfluous
Some(dummyValue, (Some(dummyValue), restOfValues))
else
Some(currentValue, (Some(currentValue), Seq.skip 1 restOfValues))) // Either the two values were contiguous, or the gap between them was too large to patch
// Method 2 (Fast)
let insertDummyValuesWhereASingleValueIsMissing2 (timeBetweenContiguousValues : TimeSpan) (values : (DateTime * float) list) =
let sizeOfHolesToPatch = timeBetweenContiguousValues.Add timeBetweenContiguousValues // Only insert dummy-values when the gap is twice the normal
(None, values) |> Seq.unfold (fun (prevValue, restOfValues) ->
match restOfValues with
| [] -> None // Reached the end of the input list
| currentValue::restOfValues ->
if prevValue.IsNone then
Some(currentValue, (Some(currentValue), restOfValues )) // Only happens to the first item in the list
else
let currentTime = fst currentValue
let prevTime = fst prevValue.Value
let timeDiffBetweenPrevAndCurrentValue = currentTime.Subtract(prevTime)
if timeDiffBetweenPrevAndCurrentValue = sizeOfHolesToPatch then
let dummyValue = (prevTime.Add timeBetweenContiguousValues, 42.0)
Some(dummyValue, (Some(dummyValue), currentValue::restOfValues))
else
Some(currentValue, (Some(currentValue), restOfValues))) // Either the two values were contiguous, or the gap between them was too large to patch
// Test data
let numbers = {1.0..10000.0}
let contiguousTimeStamps = seq { for n in numbers -> DateTime.Now.AddMinutes(n)}
let dataWithOccationalHoles = Seq.zip contiguousTimeStamps numbers |> Seq.filter (fun (dateTime, num) -> num % 77.0 <> 0.0) // Has a gap in the data every 77 items
let timeBetweenContiguousValues = (new TimeSpan(0,1,0))
// The fast sequence-patching (method 2)
dataWithOccationalHoles |> List.of_seq |> insertDummyValuesWhereASingleValueIsMissing2 timeBetweenContiguousValues |> Seq.iter (fun pair -> printfn "%f %s" (snd pair) ((fst pair).ToString()))
// The SLOOOOOOW sequence-patching (method 1)
dataWithOccationalHoles |> insertDummyValuesWhereASingleValueIsMissing1 timeBetweenContiguousValues |> Seq.iter (fun pair -> printfn "%f %s" (snd pair) ((fst pair).ToString()))
+1: F # yi öğrenirken, tüm zorunlu yapıları ortadan kaldırarak fonksiyonel programlama oluğuna girdim. Kodumun okunabilirliğini sonsuz derece daha basit "döngü ve ref" yaklaşımından ziyade Seq.unfold kullanarak bir noseratör aldığını izledim. – Juliet
Jason, aradığım çözüm bu. Yöntemi yazarken ilk eğimim verim kullanmaktı (C# arka planından geliyorum), fakat F # -book'um yokken (Don Syme'nin Aralık deklarasyonunu beklerken) F # 'nin verimi nasıl kullandığını anlayamadım. Seq.unfold. – Treefrog
@TreeFrog. daha da iyi f # verim var! 'hangi 'verim foreach' d için onlar c ekleyeceklerini dile getiriyordum # # – ShuggyCoUk