Как реализовать «где» (numpy.where (...))? - PullRequest
2 голосов
/ 19 декабря 2011

Я новичок в функциональном программировании. Я хотел бы знать, как реализовать numpy.where () в python, scala или haskell. Хорошее объяснение будет полезно для меня.

Ответы [ 3 ]

6 голосов
/ 19 декабря 2011

В 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 (благодарность Хоакину за указание на это):

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

Для достижения случая с двумя аргументами (возвращая все индексы, где условие истинно; спасибо Рексу Керру за указание этого случая), можно использовать понимание списка:

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-мерный тип массива на практике. Если вы просто хотите использовать select в n-мерном списке для одного конкретного n , совет Луки (из комментариев к этому ответу) предпочтителен:

На практике вместо взлома класса типов я бы использовал (zipWith3.zipWith3.zipWith3) select' bs xs ys (для трехмерного случая).

(добавление новых композиций zipWith3 по мере увеличения n .)

5 голосов
/ 19 декабря 2011

В питоне от numpy.where.__doc__:

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)]
3 голосов
/ 19 декабря 2011

Есть два варианта использования где; в одном случае у вас есть два массива, а в другом - только один.

В случае с двумя элементами, numpy.where(cond), вы получите список индексов, где массив условий равен true. В 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 и выдает true или false, а затем вы

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

(хотя обычно, даже будучи кратким, вы называете массивы xs и ys и отдельные элементы x и y).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...