如何实现“where”(numpy.where(...))?

时间:2011-12-18 20:45:41

标签: python scala haskell functional-programming numpy

我是一名功能性编程新手。 我想知道如何在python,scala或haskell中实现numpy.where()。 一个好的解释对我有帮助。

3 个答案:

答案 0 :(得分:6)

在Haskell中,对于n维列表来说,就像NumPy等效支持一样,需要一个相当高级的类型类构造,但是1维案例很容易:

select :: [Bool] -> [a] -> [a] -> [a]
select [] [] [] = []
select (True:bs) (x:xs) (_:ys) = x : select bs xs ys
select (False:bs) (_:xs) (y:ys) = y : select bs xs ys

这只是一个简单的递归过程,依次检查每个列表的每个元素,并在每个列表到达结尾时生成空列表。 (请注意,这些是列表,而不是数组。)

这是一个更简单但不太明显的一维列表实现,翻译NumPy文档中的定义(信用到joaquin指出):

select :: [Bool] -> [a] -> [a] -> [a]
select bs xs ys = zipWith3 select' bs xs ys
  where select' True x _ = x
        select' False _ y = y

要实现两参数的情况(返回条件为True的所有索引;归功于Rex Kerr指出这种情况),可以使用列表推导:

trueIndices :: [Bool] -> [Int]
trueIndices bs = [i | (i,True) <- zip [0..] bs]

它也可以用现有的select编写,虽然没有多大意义:

trueIndices :: [Bool] -> [Int]
trueIndices bs = catMaybes $ select bs (map Just [0..]) (repeat Nothing)

这是n维列表的三参数版本:

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}

class Select bs as where
  select :: bs -> as -> as -> as

instance Select Bool a where
  select True x _ = x
  select False _ y = y

instance (Select bs as) => Select [bs] [as] where
  select = zipWith3 select

以下是一个例子:

GHCi> select [[True, False], [False, True]] [[0,1],[2,3]] [[4,5],[6,7]]
[[0,5],[6,3]]

但是,您可能希望在实践中使用正确的n维数组类型。如果你只想在一个特定的 n 的n维列表中使用select,那么luqui的建议(来自这个答案的评论)是可取的:

  

在实践中,我会使用(zipWith3.zipWith3.zipWith3) select' bs xs ys(对于三维情况)而不是类型类黑客。

(添加更多zipWith3 n 的成分会增加。)

答案 1 :(得分:5)

来自numpy.where.__doc__的python:

If `x` and `y` are given and input arrays are 1-D, `where` is
equivalent to::

    [xv if c else yv for (c,xv,yv) in zip(condition,x,y)]

答案 2 :(得分:3)

有两个用例在哪里;在一种情况下,你有两个数组,而在另一种情况下,你只有一个。

在两项案例numpy.where(cond)中,您将获得条件数组为true的索引列表。在Scala中,您通常会

(cond, cond.indices).zipped.filter((c,_) => c)._2

显然不那么紧凑,但这不是人们通常在Scala中使用的基本操作(例如,构建块不同,不再强调索引)。

在三项内容numpy.where(cond,x,y)中,您可以获得xy,具体取决于cond是真(x)还是假( y)。在Scala中,

(cond, x, y).zipped.map((c,tx,ty) => if (c) tx else ty)

执行相同的操作(再次不那么紧凑,但同样,通常不是基本操作)。请注意,在Scala中,您可以更轻松地将cond作为测试xy并生成true或false的方法,然后您将

(x, y).zipped.map((tx,ty) => if (c(tx,ty)) tx else ty)

(尽管通常即使简短,您也可以将数组命名为xsys以及各个元素xy。)