我想出了一种在数据的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