如何使用Pipes库编写GroupBy函数

时间:2019-08-14 13:52:16

标签: haskell haskell-pipes haskell-frames

我想出了一种在数据的InCore表示形式上实现此功能的方法,如下所示:

groupByCol :: (Eq a, Ord a, RecVec rs) =>
             (forall (f :: * -> *).
                 Functor f =>
                 (a -> f a) -> Record rs -> f (Record rs))
             -> FrameRec rs -> Map a (FrameRec rs)
groupByCol feature frame = M.map toFrame $ F.foldl' groupBy M.empty frame
  where groupBy m r = M.insertWith (\[new] old -> new:old) (view feature r) [r] m

并且下面的单元测试通过了(所以您可以看到一个示例,说明如何使用它以及预期的结果)。

  describe "Lib.groupByCol" $ do
    it "Returns the expected map when splitting the Spam dataset on the Images column" $ do
      -- type SpamOrHam = Record '[SpamId :-> Int, SuspiciousWords :-> Bool, UnknownSender :-> Bool, Images :-> Bool, SpamClass :-> Text]
      spamFrame <- loadSpamOrHam
      let row0 :: SpamOrHam
          row0 = 489 &: True &: True &: False &: "spam" &: RNil
          row1 :: SpamOrHam
          row1 = 376 &: True &: False &: True &: "spam" &: RNil
          groupingMap = groupByCol images spamFrame
          falseRows = F.toList $ groupingMap M.! False
          trueRows = F.toList $ groupingMap M.! True
      falseRows `shouldContain` [row0]
      falseRows `shouldNotContain` [row1]
      trueRows `shouldContain` [row1]
      trueRows `shouldNotContain` [row0]

因此,我想使用Pipes库提供的函数来创建等效的groupByCol函数,因为Frames内部使用该库来优化其计算。以下是我的最佳尝试,但似乎Pipes库中的fold为我处理了将数据转换为Map的问题,因为类型系统说我正在找回Map a (Map a (Record rs))而不是Map a [Record rs],而后者就是目标。我很确定这种自动转换不会自动根据提供的列中的唯一值对数据集进行分区。

groupByCol' :: (Eq a, Ord a, RecVec rs) =>
             (forall (f :: * -> *).
                 Functor f =>
                 (a -> f a) -> Record rs -> f (Record rs))
             -> FrameRec rs -> Map a (FrameRec rs)
groupByCol' feature frame =
  P.fold groupBy M.empty (M.map toFrame) (P.each frame)
    where groupBy m r = M.insertWith (\[new] old -> new:old) (view feature r) [r] m

这里是完整的代码,如果您需要进一步的上下文。我不介意分享,因为这只是个人探索。 https://github.com/josiah14-MachineLearning/ID3-and-Derivatives/blob/master/ID3/haskell/sequential/hid3-and-seq/src/Lib.hs#L116


更新

以下代码利用Identity Monad取得了一些成功,但是我真的很想摆脱使用groupBy嵌套函数中的List并在那里使用Pipes的麻烦。我以为使用Pipes.yield是可行的方法,但我无法满足类型检查器的要求。

groupByCol' :: (Eq a, Ord a, RecVec rs) =>
             (forall (f :: * -> *).
                 Functor f =>
                 (a -> f a) -> Record rs -> f (Record rs))
             -> FrameRec rs -> Map a (FrameRec rs)
groupByCol' feature frame =
  runIdentity $ P.fold groupBy M.empty (M.map toFrame) (P.each frame)
    where groupBy m r = M.insertWith (\[new] old -> new:old) (view feature r) [r] m

0 个答案:

没有答案