Как заменить элементы матрицы бриза в Scala по какому-то условию?

Я работаю с двумерными матрицами Breeze в Scala. В какой-то момент мне нужно выполнить поэлементное деление двух матриц. Некоторые элементы в матрице знаменателя могут быть нулевыми, что приводит к NaN в результате.

Я могу перебрать размеры матрицы и заменить 0,0 на что-то> 0.

Но есть ли для этого более простое или идиоматическое решение Scala?


person inferno    schedule 08.04.2017    source источник


Ответы (2)


Шаг за шагом:

  • С примером матрицы:

    val dm = DenseMatrix((1.0, 0.0, 3.0), (0.0, 5.0, 6.0))
    
  • Узнайте, какие элементы равны 0.0:

    dm :== 0.0
    
    breeze.linalg.DenseMatrix[Boolean] =
    false  true   false
    true   false  false
    
  • Разрезать матрицу:

    dm(dm :== 0.0)
    
    breeze.linalg.SliceVector[(Int, Int),Double] = breeze.linalg.SliceVector@2b
    
  • Используйте нарезанную матрицу для замены:

    dm(dm :== 0.0) := 42.0
    
    breeze.linalg.Vector[Double] = breeze.linalg.SliceVector@2b
    
  • Проверьте матрицу:

    dm
    
    breeze.linalg.DenseMatrix[Double] =
    1.0   42.0  3.0
    42.0  5.0   6.0
    
person zero323    schedule 08.04.2017
comment
Благодарю вас! Это то, что я искал. - person inferno; 08.04.2017
comment
@inferno Рад это слышать. Надеюсь, вы не будете возражать, если я предложу принять и / или проголосовать за ответ. Заранее спасибо. - person zero323; 21.07.2018

Составление карты NaN выполняется быстрее, чем нарезка.

val matr = DenseMatrix((1.0, 0.0, 3.0), (0.0, 11.0, 12.0),
      (1.0, 2.0, 0.0))
val matr2 = DenseMatrix((3.0, 0.0, 1.0), (0.0, 12.0, 11.0),
      (2.0, 1.0, 0.0))

def time[R](block: => R): R = {
  val t0 = System.nanoTime()
  val result = block    // call-by-name
  val t1 = System.nanoTime()
  println("Elapsed time: " + (t1 - t0) + "ns")
  result
}

def replaceZeroes1(mat1: DenseMatrix[Double], mat2: DenseMatrix[Double], rep: Double) = {
   (mat1 /:/ mat2).map(x => if (x.isNaN()) rep else x)
}
    
def replaceZeroes2(mat1: DenseMatrix[Double], mat2: DenseMatrix[Double], rep: Double) = {
    mat1(mat1 :== 0.0) := rep
    mat2(mat2 :== 0.0) := 1
    mat1 /:/ mat2
}
time(println(replaceZeroes1(matr, matr2, 42.0)))
time(println(replaceZeroes2(matr, matr2, 42.0)))

Производит:

0.3333333333333333  42.0                3.0                 
42.0                0.9166666666666666  1.0909090909090908  
0.5                 2.0                 42.0                
Elapsed time: 13087782ns
Replace Zero2
0.3333333333333333  42.0                3.0                 
42.0                0.9166666666666666  1.0909090909090908  
0.5                 2.0                 42.0                
Elapsed time: 16613179ns

Сопоставление NaN выполняется быстрее и проще. Это быстрее, даже если вы удалите второй фрагмент из function2.

ПРИМЕЧАНИЕ. Это не тестировалось в Spark с очень большими наборами данных, просто бриз. В таком случае возможно, что времена разные (хотя я в этом сомневаюсь).

БОНУС:

Если вы просто пытаетесь создать матрицу 1 и 0 из матрицы с любым набором значений (например, создать невзвешенную сеть из взвешенной сети), я бы просто использовал:

(mat /:/ mat).map(x => if (x.isNaN()) 0.0 else x)
person Ryan Deschamps    schedule 15.09.2019