我正在寻找有关何时在Julia中使用missing
,nothing
,undef
和NaN
的一些指导。
例如,对于预分配数组或从try
/ catch
返回,似乎都是合理的选择。
TLDR:
如果您从事统计工作,可能是希望missing
表示集合中没有特定数据。
如果要定义浮点数数组,但稍后再初始化单个元素,则出于性能方面的考虑,可能要使用undef
(以避免花时间将元素设置为一个值,此后将被覆盖):
Vector{Float64}(undef, n)
[对于性能不太注重而对安全性更高的方法,您也可以将所有元素初始化为NaN
,以便利用Nan
的传播行为来帮助识别如果忘记设置一些错误可能会发生的错误。数组中的值:
fill(NaN, n)
您可能会在Julia自己的API的某些部分遇到nothing
,但通常不会在数组中使用它,否则会获取数字数据(这里似乎是您的用例)
这是我对这些选项之间的区别的看法:
missing
用于以统计意义表示missing values,即理论上存在的值,但您不知道。 missing
的实质(在大多数情况下,在行为上)与R中的NA
相似。NA
值的定义特征是可以在计算中使用它们:
missing
julia> x = 1 # x has a known value: 1
1
julia> y = missing # y has a value, but it is unknown
missing
julia> z = x * y # no error: z has a value, that just happens to be unknown (as a consequence of not knowing the value of y
missing
的一个重要特征是它具有自己的特定类型:missing
。特别是这意味着包含Missing
值和其他数值的数组在类型上不是同质的:
missing
julia> [1, missing, 3]
3-element Array{Union{Missing, Int64},1}: # not Array{Int64, 1}
1
missing
3
也有自己的类型:nothing
。与Nothing
相比,它倾向于用于没有价值的事物。这就是为什么与missing
相比,使用missing
进行计算没有意义,并且出错的原因:
nothing
julia> 3*nothing
ERROR: MethodError: no method matching *(::Int64, ::Nothing)
主要用作不返回任何东西的函数的返回值,要么是因为它们仅具有副作用,要么是因为它们无法计算任何其他有意义的结果:
nothing
另一个julia> typeof(println("OK")) # Only side effects
OK
Nothing
julia> typeof(findfirst('a', "Hello")) # No other meaningful result
Nothing
位于函数参数或对象字段中,但不能始终为其提供值。例如,在下面对二叉树结构的定义中,叶子(按照定义,它是没有子节点的节点)将被表示为其子节点为notable use of nothing
的节点:
nothing
与前两个nothing
不同,它没有自己的特定类型。 struct TreeNode
child1 :: Union{TreeNode, Nothing}
child2 :: Union{TreeNode, Nothing}
end
leaf = TreeNode(nothing, nothing)
仅仅是NaN
类型的特定值(并且NaN
类似地存在于Float64
中)。您可能知道,这些值在浮点运算中确实具有特殊含义,这使它们传播(与NaN32
值大致相同)。但是除了算术行为外,这些都是正常的浮点值。特别是,浮点值的向量可以包含Float32
,而不会影响其类型:
missing
NaN
与到目前为止提到的所有内容都非常不同。它不是一个真正的值(至少不是在具有值的数字的意义上),而是一个“标志”,可以将其传递给数组构造函数以告诉Julia不要初始化数组中的值(通常出于性能考虑) )。在下面的示例中,数组元素将不会设置为任何特定值,但是,由于在Julia中不存在没有任何值的数字之类的东西,因此元素将具有任意值(来自向量在内存中发生的一切)被分配)。
julia> [1., NaN, 2.]
3-element Array{Float64,1}: # Note how this differs from the example with missing above
1.0
NaN
2.0
[当元素具有更复杂的类型并且已初始化和未初始化的元素之间存在区别时,Julia用undef
表示后者
julia> Vector{Float64}(undef, 3)
3-element Array{Float64,1}:
6.94567437726575e-310
6.94569509953624e-310
6.94567437549977e-310
我将总结以下选项。我写的是从“读取值”的角度来看的,但这也是“写入值”时的指导。
#undef
表示“ 该值不存在”(例如,julia> mutable struct Foo end
julia> Vector{Foo}(undef, 3)
3-element Array{Foo,1}:
#undef
#undef
#undef
在集合中找不到值时返回nothing
);它是单独的类型findfirst
nothing
的意思是“ 值本身存在,但我们不知道它>”(我希望通常只有从外部来源获取数据时,您才能在数据中获得Nothing
。缺少患者数据和体温的记录(显然它存在-只是没有记录);我不认为Base的任何函数都可以返回它,除非它以missing
作为参数。它是单独的类型missing
missing
-仅仅是数字数据(与Missing
和NaN
相反);如果对返回的数字值进行某些运算的结果,则向用户发出信号;根据我的经验,这是nothing
应该出现在数据中的唯一情况(例如missing
的结果)NaN
不是您会看到的值,它仅以例如NaN
创建数组而不初始化其值(因此,这只是性能优化);仅当您要立即使用计划计算的某些值初始化数组的元素时,才应使用它(并且如果数组的元素类型不是位类型或联合,则使用0/0
将导致undef
条目位类型;对于使用Vector{Int}(undef, 10)
初始化数组的位类型,只会给您带来一些垃圾)这些是标准规则。现在是一个例外(这是其他一些语言的典型做法),有时您可能希望使用undef
来表示集合中的#undef
或undef
。不建议这样做,但是它有一个好处,您可以在此示例中看到:
NaN
并且您可以看到missing
是浮点值,nothing
数组的元素类型只是julia> x1 = [1.0, NaN]
2-element Array{Float64,1}:
1.0
NaN
julia> x2 = [1.0, missing]
2-element Array{Union{Missing, Float64},1}:
1.0
missing
,而在NaN
数组中,元素类型是x1
。在某些情况下,您可能希望选择Float64
而不是x2
,因为针对它执行操作会更快(例如,检查Union
的可能性具有最小的开销)。但这是通常不应该执行的性能优化,因为其他人在阅读Julia代码时通常会认为x1
是真正的x2
,而不是表示missing
或NaN
的占位符。