朱莉娅

时间:2020-05-21 14:06:29

标签: julia

我正在寻找有关何时在Julia中使用missingnothingundefNaN的指导。

例如,对于预分配数组或从try / catch返回,似乎都是合理的选择。

2 个答案:

答案 0 :(得分:11)

TLDR:

  • 如果您从事统计工作,则很可能希望missing表示集合中没有特定数据。

  • 如果要定义浮点数数组,但稍后再初始化单个元素,则出于性能方面的考虑,可能要使用undef(为避免花时间将元素设置为一个值,将在之后被覆盖):

    Vector{Float64}(undef, n)
    

    在相同的情况下,但遵循一种不太注重性能而更注重安全的方法,您也可以将所有元素初始化为NaN,以便利用NaN的传播行为来提供帮助识别如果忘记在数组中设置一些值可能会发生的错误:

    fill(NaN, n)
    
  • 您可能会在Julia的API的某些部分遇到nothing,以表示无法计算有意义的值的情况。但这通常不用于数组中,否则会包含数值数据(这似乎是您的用例)


以下是这些选项之间的区别:




missing用于从统计意义上表示missing values,即理论上存在但您不知道的值。 missing的实质(在大多数情况下,在行为上)与R中的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
missing            # (as a consequence of not knowing the value of y

missing的一个重要特征是它具有自己的特定类型:Missing。特别是这意味着在其他数值中包含missing值的数组在类型上不是同质的:

julia> [1, missing, 3]
3-element Array{Union{Missing, Int64},1}: # not Array{Int64, 1}
 1
 missing
 3

请注意,尽管Julia编译器已经非常擅长为此类小型联合处理此类异构数组,但是存在不同类型元素的内在性能问题,因为我们无法提前知道元素的类型会。




nothing也有自己的类型:Nothing。与missing相比,它倾向于用于things that have no value。这就是为什么与missing相比,使用nothing进行计算没有意义,并且出错的原因:

julia> 3*nothing
ERROR: MethodError: no method matching *(::Int64, ::Nothing)

nothing主要用作不返回任何东西的函数的返回值,要么是因为它们仅具有副作用,要么是因为它们无法计算任何有意义的结果:

julia> @show println("OK")           # Only side effects
OK
println("OK") = nothing

julia> @show findfirst('a', "Hello") # No meaningful result
findfirst('a', "Hello") = nothing

nothing的另一个显着用途是在函数参数或对象字段中,这些参数并不总是为其提供值。通常在类型系统中将其表示为Union{MeaningfulType, Nothing}。例如,在下面对二叉树结构的定义中,叶子(按照定义是没有子节点的叶子)将被表示为子节点为nothing的节点:

struct TreeNode
  child1 :: Union{TreeNode, Nothing}
  child2 :: Union{TreeNode, Nothing}
end

leaf = TreeNode(nothing, nothing)




与前两个不同,NaN没有它自己的特定类型:NaN仅仅是Float64类型的特定值(并且NaN32对于{ {1}})。您可能知道,这些值通常显示为未定义运算的结果(例如0/0),并且在浮点算术中具有非常特殊的含义,使它们传播(与{{ 1}}值)。但是除了算术行为外,这些都是正常的浮点值。特别是,浮点值向量可以包含Float32,而不会影响其类型:

missing




NaN与到目前为止提到的所有内容都大不相同。它不是真正的值(至少不是在具有值的数字意义上),而是一个“标志”,可以传递给array constructors来告诉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

当元素具有更复杂的类型(用专业术语表示:非isbits类型)并且可以区分初始化元素和未初始化元素时,Julia用undef表示后者。

julia> Vector{Float64}(undef, 3)
3-element Array{Float64,1}:
 6.94567437726575e-310
 6.94569509953624e-310
 6.94567437549977e-310

答案 1 :(得分:7)

我将总结以下选项。我写的是从“读取值”的角度来看的,但这也是“写入值”时的指导。

  1. nothing表示“ 该值不存在”(例如,findfirst在集合中找不到值时返回nothing);它是单独的类型Nothing
  2. missing的意思是“ 值本身存在,但我们不知道”(我希望通常只有从中获取missing的数据外部来源,例如您有一个病人数据记录,并且体温丢失(显然它存在-只是没有被记录);我不认为Base的任何函数都可以返回它,除非它得到{{1 }}作为参数);它是单独的类型missing
  3. Missing-只是一个数字数据(与NaNnothing相对);如果对返回的数字missing进行某些运算的结果,则向用户发出信号;根据我的经验,这是NaN应该出现在您数据中的唯一情况(例如NaN的结果)
  4. 0/0不是您会看到的值,它仅以以下形式使用: undef创建一个数组而不初始化其值(因此这只是性能优化);仅当您要立即使用计划计算的某些值初始化数组的元素时,才应使用它(并且如果数组的元素类型不是位,则使用Vector{Int}(undef, 10)将导致undef条目类型或位类型的并集;对于使用#undef初始化数组的位类型,只会给您带来一些麻烦)

这些是标准规则。现在是一个例外(这是其他一些语言的典型做法),有时您可能想使用undef来表示集合中的NaNmissing。不建议这样做,但是它有一个好处,在本示例中可以看到:

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而不是x1,因为针对它执行操作会更快一些(例如检查x2的可能性开销很小)。但这通常是不应该执行的性能优化,因为其他人在阅读Julia代码时通常会认为missing是真正的NaN,而不是表示NaN或{的占位符{1}}。