Логическая индексация в Юлии - PullRequest
3 голосов
/ 01 октября 2019

В MATLAB для удаления всех строк матрицы, имеющих значения NaN, я пишу следующий код:

myMatrix( any(isnan(myMatrix), 2), :) = [] 

Где:

any(isnan(myMatrix), 2) 

вернуть логический вектор

Есть ли способ сделать это в Джулии?

Кажется, я не могу найти способ сделать это в Джулии. Поэтому я вынужден писать некрасивые петли.

Ответы [ 3 ]

3 голосов
/ 01 октября 2019

Вы можете использовать вещание для достижения этой цели:

julia> x = rand([NaN; 1:10], 10, 4)
10×4 Array{Float64,2}:
 4.0    9.0   2.0    6.0
 3.0   10.0   2.0    2.0
 3.0    1.0   3.0    6.0
 7.0    8.0   5.0   10.0
 5.0   10.0  10.0  NaN
 4.0    3.0   7.0    5.0
 1.0    8.0   9.0    4.0
 6.0  NaN     3.0    5.0
 9.0  NaN     7.0    1.0
 9.0    4.0   6.0   10.0

julia> x[.!any.(isnan, eachrow(x)), :]
7×4 Array{Float64,2}:
 4.0   9.0  2.0   6.0
 3.0  10.0  2.0   2.0
 3.0   1.0  3.0   6.0
 7.0   8.0  5.0  10.0
 4.0   3.0  7.0   5.0
 1.0   8.0  9.0   4.0
 9.0   4.0  6.0  10.0

или

julia> x[vec(.!any(isnan.(x), dims=2)), :]
7×4 Array{Float64,2}:
 4.0   9.0  2.0   6.0
 3.0  10.0  2.0   2.0
 3.0   1.0  3.0   6.0
 7.0   8.0  5.0  10.0
 4.0   3.0  7.0   5.0
 1.0   8.0  9.0   4.0
 9.0   4.0  6.0  10.0
2 голосов
/ 01 октября 2019

Улучшен ответ Bogumił Kamiński и добавлены некоторые тесты.

Это, кажется, самый быстрый:

# !any_func is equal to `(x)-> !any_func(x)`
x[vec(all(!isnan, x, dims = 2)), :] 
using BenchmarkTools
A = rand(1000, 1000)
function version1(x)
    x[.!any.(isnan, eachrow(x)), :]
end
function version2(x)
    x[vec(.!any(isnan.(x), dims = 2)), :]
end
function version3(x)
    x[all.(!isnan, eachrow(x)), :]
end
function version4(x)
    x[vec(all(!isnan, x, dims = 2)), :]
end
x = rand(1000, 1000)
@btime version1($A);
@btime version2($A);
@btime version3($A);
@btime version4($A);
5.439 ms (1011 allocations: 7.69 MiB)
6.370 ms (21 allocations: 7.75 MiB)
5.548 ms (1011 allocations: 7.69 MiB)
4.119 ms (14 allocations: 7.63 MiB)
2 голосов
/ 01 октября 2019

Да, просто используйте логический массив в качестве индекса.

julia> v = rand(5)
5-element Array{Float64,1}:
 0.6377159558422454
 0.1205285547043713
 0.04902451987818024
 0.737928505686815
 0.34881071296002175

julia> i = v .> 0.5
5-element BitArray{1}:
 1
 0
 0
 1
 0

julia> v[i]
2-element Array{Float64,1}:
 0.6377159558422454
 0.737928505686815

То же самое работает с двумерными массивами:

julia> m = rand(3,2)
3×2 Array{Float64,2}:
 0.377744  0.0296205
 0.682967  0.366501
 0.906793  0.791147

julia> m[[true,true,false],:]
2×2 Array{Float64,2}:
 0.377744  0.0296205
 0.682967  0.366501

В julia эквивалент any(isnan(myMatrix), 2) равенвместо any(isnan, myMatrix, dims=2). Или, поскольку вы сказали, что хотите удалить эти строки, вы на самом деле хотите all(!isnan, myMatrix, dims=2) Однако, в любом случае это возвращает двумерный массив из 1 столбца, который вы не можете использовать для индексации. Вы можете либо преобразовать это в вектор, либо вместо этого отобразить это по строкам, чтобы получить вектор напрямую:

julia> myMatrix = rand([NaN, 1:5...], 5,2)
5×2 Array{Float64,2}:
 1.0    3.0
 5.0  NaN
 4.0    1.0
 5.0  NaN
 1.0    2.0

julia> rowfilter = all(!isnan, myMatrix, dims=2)[:,1]
5-element Array{Bool,1}:
 1
 0
 1
 0
 1

julia> myMatrix[rowfilter, :]
3×2 Array{Float64,2}:
 1.0  3.0
 4.0  1.0
 1.0  2.0

или

julia> myMatrix[map(row-> all(!isnan, row), eachrow(myMatrix)), :]
3×2 Array{Float64,2}:
 1.0  3.0
 4.0  1.0
 1.0  2.0

или с широковещательной рассылкой вместо map():

julia> myMatrix[all.(!isnan, eachrow(myMatrix)), :]
3×2 Array{Float64,2}:
 1.0  3.0
 4.0  1.0
 1.0  2.0
...