4

現在、次のようなデータがあります。

3-150
2-151
4-152
5-154
7-154
1-155
9-155
6-156

これは単なる人工的な「ティック」データであり、最初のデータはティックの値を表し、2 番目のデータは「午前 0 時からの秒数」を表します。

株式データの場合、このデータを「バー」に分類する必要があります。つまり、特定の時間のすべてのバーをグループ化する必要があります。

例として、4 秒バーがあります。真夜中の 0 ~ 3 秒後のティックは 1 つのバーになり、真夜中の 4 ~ 7 秒後のティックは別のバーになります。

1 つのバー サイズを計算する、次のようなコンジット/シンクがあります。

{-# LANGUAGE OverloadedStrings #-}

import Data.Maybe (isJust, fromJust)
import qualified Data.ByteString.Char8 as C
import Control.Applicative ((<$>), (<*>))

import Data.Conduit -- the core library
import qualified Data.Conduit.List as CL -- some list-like functions
import qualified Data.Conduit.Binary as CB -- bytes
import qualified Data.Conduit.Text as CT

data MyData = MyData Int Int
    deriving (Show)

binaryToData :: C.ByteString -> Maybe MyData
binaryToData bn = do
    let parts = C.split '-' bn
    case parts of
        (a:b:[]) -> MyData <$> (fst <$> (C.readInt a)) <*> (fst <$> (C.readInt b))
        _ -> Nothing

streamGenerator =
    CB.sourceFile "sample.txt" =$=
    CB.lines =$=
    CL.map binaryToData =$=
    CL.filter isJust =$=
    CL.map fromJust =$=
    CL.groupBy (\(MyData _ x) (MyData _ y) -> (x `quot` 4) == (y `quot` 4))

main :: IO ()
main = do
    mlines <- runResourceT $ streamGenerator $$ CL.consume
    print mlines

ただし、ストリームから同時に複数のバー情報が必要です。たとえば、2 秒バーごとに 4 秒バーが必要です。呼び出されている 2 秒足が 4 秒足の途中にある場合、前の 4 秒足を出力したいと思います。

これが私が意味することです:

標準バー (数値は、バーに含める必要のある真夜中を過ぎた秒単位のティックを意味します):

2 second bar : 0-1, 2-3, 4-5, etc...
4 second bar : 0-3, 4-7, 8-11, etc...
combo: (0-1, null), (2-3, 0-3), (4-5, 0-3),  (6-7, 4-7), etc... 

したがって、2 秒と 4 秒のバーのグループ化の現在のコンジットの代わりに:

4 second bar : [[MyData 3 150,MyData 2 151],[MyData 4 152,MyData 5 154,MyData 7 154,MyData 1 155,MyData 9 155],[MyData 6 156]]
2 second bar : [[MyData 3 150,MyData 2 151],[MyData 4 152],[MyData 5 154,MyData 7 154,MyData 1 155,MyData 9 155],[MyData 6 156]]

このコンジット ストリームが必要です:

[([MyData 3 150,MyData 2 151], [MyData 3 150,MyData 2 151])
,([MyData 4 152], [MyData 3 150,MyData 2 151])
,([MyData 5 154,MyData 7 154,MyData 1 155,MyData 9 155], [MyData 4 152,MyData 5 154,MyData 7 154,MyData 1 155,MyData 9 155])
,([MyData 6 156],[MyData 4 152,MyData 5 154,MyData 7 154,MyData 1 155,MyData 9 155])]

しかし、私は醜いことをしない限り、それを完全に行うことはできないようです.

4

1 に答える 1

3

pipesよろしければ、ライブラリを使用して質問に答えることができます。それが私にとって快適なことだからです。必要に応じて、このソリューションをに翻訳できconduitます。

この必要なプッシュバックに対するクリーンなソリューションですが、pipesまだプッシュバックがないため、先に進んで実装しました(近い将来、拡張ライブラリとして含める予定です)。

import Control.Monad
import Control.Proxy
import Control.Proxy.Trans.State

-- Pushback primitives, soon to be in a `pipes` library

require :: (Monad m, Proxy p) => a' -> StateP [a] p a' a b' b m a
require a' = StateP $ \s -> runIdentityP $ do
    case s of
        [] -> do
            a <- request a'
            return (a, s)
        a:as -> do
            return (a, as)

pushback :: (Monad m, Proxy p) => a -> StateP [a] p a' a b' b m ()
pushback a = StateP $ \as -> runIdentityP $ return ((), a:as)

evalPushback = evalStateK []

それらが手元にあれば、解決策は簡単です。

data MyData = MyData Int Int deriving (Eq, Show)

-- Consumes ticks up until the deadline or the end of input
-- Returns the list of all ticks before the deadline
ticksUntil
 :: (Monad m, Proxy p)
 => Int -> () -> Consumer (StateP [Maybe MyData] p) (Maybe MyData) m [MyData]
ticksUntil deadline () = go where
    go = do
        x <- require ()
        case x of
            Just m@(MyData _ time) ->
                if (time < deadline)
                then do
                    ms <- go
                    return (m:ms)
                else do
                    pushback x
                    return []
            Nothing -> return []

bars
 :: (Monad m, Proxy p)
 => () -> Pipe (StateP [Maybe MyData] p) (Maybe MyData) ([MyData], [MyData]) m r
bars () = loop1 2 [] where
    -- First half of a 4-second window
    loop1 deadline b4 = do
        b2 <- (ticksUntil deadline >-> unitU) ()
        respond (b2, b4)
        loop2 (deadline + 2) b2 b4

    -- Second half of a 4-second window
    loop2 deadline b2 b4 = do
        b2' <- (ticksUntil deadline >-> unitU) ()
        let b4' = b2 ++ b2'
        respond (b2', b4')
        loop1 (deadline + 2) b4'

sample :: [MyData]
sample = [
    MyData 3 150,
    MyData 2 151,
    MyData 4 152,
    MyData 5 154,
    MyData 7 154,
    MyData 1 155,
    MyData 9 155,
    MyData 6 156]

-- Use the same trick as conduit: Nothing signals termination
source :: (Monad m, Proxy p) => () -> Producer p (Maybe MyData) m ()
source () = runIdentityP $ do
    (fromListS sample >-> mapD Just) ()
    respond Nothing

main = runProxy $
     source                 -- feed sample data
 >-> evalPushback bars      -- group the data into bars
 >-> filterD (/= ([], []))  -- Ignore empty bars
 >-> printD                 -- print outgoing bars

魔法はbars機能にあります。2つの状態を切り替えるだけです。 loop1は、4つのセットのうち2つの値の最初のバーを期待している最初の状態であり、はloop22つの値の2番目のバーを期待している2番目の状態です。

これを実装する上で最も難しい部分は、コードを書くことではなく、仕様を理解することでした。幸いなことに、私のコードはテスト例とまったく同じ動作を生成するため、あなたが何を意味するのか理解できたと思います。

>>> main
([MyData 3 150,MyData 2 151],[MyData 3 150,MyData 2 151])
([MyData 4 152],[MyData 3 150,MyData 2 151])
([MyData 5 154,MyData 7 154,MyData 1 155,MyData 9 155],[MyData 4 152,MyData 5 154,MyData 7 154,MyData 1 155,MyData 9 155])
([MyData 6 156],[MyData 4 152,MyData 5 154,MyData 7 154,MyData 1 155,MyData 9 155])

これに興味がある場合は、ライブラリ、特にControl.Proxy.Tutorialpipesのチュートリアルを確認することをお勧めします。このチュートリアルでは、コードで使用する多くのイディオムについて説明しています。pipes

于 2012-12-23T22:46:55.740 に答える