【发布时间】:2011-12-18 20:45:40
【问题描述】:
我是一个函数式编程新手。 我想知道如何在 python、scala 或 haskell 中实现 numpy.where() 。 一个好的解释会对我有所帮助。
【问题讨论】:
标签: python scala haskell functional-programming numpy
我是一个函数式编程新手。 我想知道如何在 python、scala 或 haskell 中实现 numpy.where() 。 一个好的解释会对我有所帮助。
【问题讨论】:
标签: python scala haskell functional-programming numpy
在 Haskell 中,为 n 维列表做这件事,正如 NumPy 等价物所支持的那样,需要相当高级的类型类构造,但一维的情况很容易:
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 的建议(来自此答案的 cmets)更可取:
在实践中,我会使用
(zipWith3.zipWith3.zipWith3) select' bs xs ys(对于三维情况)而不是 typeclass hack。
(随着 n 的增加,添加更多 zipWith3 的组合。)
【讨论】:
(zipWith3.zipWith3.zipWith3) select' bs xs ys(对于三维情况)而不是 typeclass hack。如果我在写的时候不知道维数,那么,如你所说,我会使用合适的抽象类型。
numpy.where...谢谢你让我知道zipWith3 可以在这里工作,顺便说一句!我已经相应地编辑了我的答案,并包含了您的评论。 :)
来自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)]
【讨论】:
where 有两个用例;在一种情况下,您有两个数组,而在另一种情况下,您只有一个。
在两个项目的情况下,numpy.where(cond),您将获得条件数组为真的索引列表。在 Scala 中,您通常会
(cond, cond.indices).zipped.filter((c,_) => c)._2
这显然不太紧凑,但这不是人们通常在 Scala 中使用的基本操作(例如,构建块不同,不强调索引)。
在三项情况下,numpy.where(cond,x,y),您会得到 x 或 y,具体取决于 cond 是真 (x) 还是假 (y)。在 Scala 中,
(cond, x, y).zipped.map((c,tx,ty) => if (c) tx else ty)
执行相同的操作(同样不那么紧凑,但通常不是基本操作)。请注意,在 Scala 中,您可以更轻松地让 cond 成为测试 x 和 y 并产生真假的方法,然后您就可以
(x, y).zipped.map((tx,ty) => if (c(tx,ty)) tx else ty)
(尽管通常即使很简短,您也会将数组命名为 xs 和 ys 以及单个元素 x 和 y)。
【讨论】: