2016-11-13 78 views
4

是否有與NumPy的argsort函數等價的標準Haskell?高效的Haskell相當於NumPy的參數

我使用的是HMatrix,所以想要一個兼容Vector R的函數,它是Data.Vector.Storable.Vector Double的別名。下面的argSort功能是我目前使用的實現:

{-# LANGUAGE NoImplicitPrelude #-} 

module Main where 

import qualified Data.List as L 
import qualified Data.Vector as V 
import qualified Data.Vector.Storable as VS 
import   Prelude (($), Double, IO, Int, compare, print, snd) 

a :: VS.Vector Double 
a = VS.fromList [40.0, 20.0, 10.0, 11.0] 

argSort :: VS.Vector Double -> V.Vector Int 
argSort xs = V.fromList (L.map snd $ L.sortBy (\(x0, _) (x1, _) -> compare x0 x1) (L.zip (VS.toList xs) [0..])) 

main :: IO() 
main = print $ argSort a -- yields [2,3,1,0] 

我使用明確的合格import的只有我要說清楚,每一個類型和功能的來源。

該實現不是非常有效,因爲它將輸入向量轉換爲列表並將結果轉換回向量。有沒有像這樣(但更高效)的地方存在?

更新

@leftaroundabout有一個很好的解決方案。這是我結束了的溶液:

module LAUtil.Sorting 
    (IndexVector 
    , argSort 
) 
    where 

import   Control.Monad 
import   Control.Monad.ST 
import   Data.Ord 
import qualified Data.Vector.Algorithms.Intro as VAI 
import qualified Data.Vector.Storable as VS 
import qualified Data.Vector.Unboxed as VU 
import qualified Data.Vector.Unboxed.Mutable as VUM 
import   Numeric.LinearAlgebra 

type IndexVector = VU.Vector Int 

argSort :: Vector R -> IndexVector 
argSort xs = runST $ do 
    let l = VS.length xs 
    t0 <- VUM.new l 
    forM_ [0..l - 1] $ 
     \i -> VUM.unsafeWrite t0 i (i, (VS.!) xs i) 
    VAI.sortBy (comparing snd) t0 
    t1 <- VUM.new l 
    forM_ [0..l - 1] $ 
     \i -> VUM.unsafeRead t0 i >>= \(x, _) -> VUM.unsafeWrite t1 i x 
    VU.freeze t1 

這是更直接地與可用Numeric.LinearAlgebra由於數據載體是Storable。這爲索引使用了unboxed向量。

回答

5

使用vector-algorithms

import Data.Ord (comparing) 

import qualified Data.Vector.Unboxed as VU 
import qualified Data.Vector.Algorithms.Intro as VAlgo 

argSort :: (Ord a, VU.Unbox a) => VU.Vector a -> VU.Vector Int 
argSort xs = VU.map fst $ VU.create $ do 
    xsi <- VU.thaw $ VU.indexed xs 
    VAlgo.sortBy (comparing snd) xsi 
    return xsi 

注意這些Unboxed而非Storable載體。後者需要做一些折衷以允許不純淨的C FFI操作,並且不能正確處理異構元組。你當然可以永遠convert往返可存儲的矢量。

0

對我來說更好的辦法是使用Data.map,因爲它受列表融合的影響,速度加快。這裏n =長度xs。

import Data.Map as M (toList, fromList, toAscList) 

    out :: Int -> [Double] -> [Int] 
    out n !xs = let !a= (M.toAscList (M.fromList $! (zip xs [0..n]))) 
        !res=a `seq` L.map snd a 
       in res 

然而,這僅僅是週期性的名單證明3,如:

out 12 [1,2,3,4,1,2,3,4,1,2,3,4] == out 12 [1,2,3,4,1,3,2,4,1,2,3,4]