如何在Julia 1.0中用数组中的NaN替换Inf值?
我在网上看到了一些解决方案如何在Julia 1.0中用数组中的NaN替换Inf值?,julia,Julia,我在网上看到了一些解决方案 a = [1 2 3; 4 5 Inf] a[isinf(a)] = NaN 但这给了我一个关于Julia 1.0.1的错误: ERROR: MethodError: no method matching isinf(::Array{Float64,2}) Closest candidates are: isinf(::BigFloat) at mpfr.jl:851 isinf(::Missing) at missing.jl:79 is
a = [1 2 3; 4 5 Inf]
a[isinf(a)] = NaN
但这给了我一个关于Julia 1.0.1的错误:
ERROR: MethodError: no method matching isinf(::Array{Float64,2})
Closest candidates are:
isinf(::BigFloat) at mpfr.jl:851
isinf(::Missing) at missing.jl:79
isinf(::ForwardDiff.Dual) at <path on my local machine>
ERROR:MethodError:没有与isinf匹配的方法(::数组{Float64,2})
最接近的候选人是:
在mpfr.jl:851处的isinf(::BigFloat)
isinf(::缺失)位于缺失处。jl:79
isinf(::ForwardDiff.Dual)位于
给出了什么?您正在将整个数组传递给isinf,它对数组无效,对数字有效。试试这个:
[isinf(i) ? NaN : i for i in a]
编辑:有关此问题最有效的解决方法,请参阅@BogumilKaminski的优秀答案。这个答案解决了一个更一般的问题,即为什么
isinf
和相关函数不再在阵列上工作
您遇到了一个更普遍的问题,即v1.0之前在阵列上工作的许多函数不再在v1.0中的阵列上工作,因为您应该使用广播。v1.0的正确解决方案是:
a[isinf.(a)] .= NaN
我实际上在这里的两个地方广播。首先,我们在数组
a
上广播isinf
,但我们也通过=
将RHS上的标量NaN
广播到LHS上数组中的所有索引位置。一般来说,点广播符号是难以置信的灵活和性能,也是我最喜欢的Julia最新版本的特征之一 作为补充评论。执行此操作的标准功能是replace代码>。您可以这样使用它:
julia> a = [1 2 3; 4 5 Inf]
2×3 Array{Float64,2}:
1.0 2.0 3.0
4.0 5.0 Inf
julia> replace!(a, Inf=>NaN)
2×3 Array{Float64,2}:
1.0 2.0 3.0
4.0 5.0 NaN
function inf2nan(x)
for i in eachindex(x)
@inbounds x[i] = ifelse(isinf(x[i]), NaN, x[i])
end
end
对于大型阵列,它的性能将优于广播
如果您确实需要速度,可以编写如下简单函数:
julia> a = [1 2 3; 4 5 Inf]
2×3 Array{Float64,2}:
1.0 2.0 3.0
4.0 5.0 Inf
julia> replace!(a, Inf=>NaN)
2×3 Array{Float64,2}:
1.0 2.0 3.0
4.0 5.0 NaN
function inf2nan(x)
for i in eachindex(x)
@inbounds x[i] = ifelse(isinf(x[i]), NaN, x[i])
end
end
现在让我们简单地比较三个选项的性能:
julia> function bench()
x = fill(Inf, 10^8)
@time x[isinf.(x)] .= NaN
x = fill(Inf, 10^8)
@time replace!(x, Inf=>NaN)
x = fill(Inf, 10^8)
@time inf2nan(x)
end
bench (generic function with 1 method)
julia> bench()
0.980434 seconds (9 allocations: 774.865 MiB, 0.16% gc time)
0.183578 seconds
0.109929 seconds
julia> bench()
0.971408 seconds (9 allocations: 774.865 MiB, 0.03% gc time)
0.184163 seconds
0.102161 seconds
这实际上创建了一个新数组,而不是更新当前数组。更多细节请参见我的答案。是的,很公平,你的答案更好。这是一个非常有用的答案。我键入我的答案时没有考虑到isinf.(x)
将分配的事实。是的,在Julia中-相反,例如R-如果需要性能,通常会希望避免分配二进制向量来执行子集设置。在这种特殊情况下,循环比replace代码>在这种情况下,因为我们可以使用ifelse
避免循环中的分支,并且由于我们执行的操作简单且@inbounds
编译器可以显著优化其执行时间。非常有趣,感谢您的解释。我将引导读者阅读您的答案,但请保留我的答案,因为它解决了问题中想知道为什么isinf
不再适用于数组的部分。您的答案是90%情况下最简单的方法,它足够好,并且是您需要知道的全部,所以+1:)。