Julia中missing、nothing、undef和NaN之间的用法和约定差异

Julia中missing、nothing、undef和NaN之间的用法和约定差异,julia,Julia,我正在寻找一些关于何时在Julia中使用missing、nothing、undef和NaN的指导 例如,对于预分配数组或从try/catch返回,所有这些似乎都是合理的选择。我将这些选项总结如下。我写的是从“阅读价值观”的角度出发,但这也是“写作价值观”的指导 nothing表示“值不存在”(例如,findfirst在集合中找不到值时返回nothing);它是一个单独的类型Nothing missing表示“值本身存在,但我们不知道它”(我希望通常只有从外部来源获取数据时,数据中才会出现miss

我正在寻找一些关于何时在Julia中使用
missing
nothing
undef
NaN
的指导


例如,对于预分配数组或从
try
/
catch

返回,所有这些似乎都是合理的选择。我将这些选项总结如下。我写的是从“阅读价值观”的角度出发,但这也是“写作价值观”的指导

  • nothing
    表示“值不存在”(例如,
    findfirst
    在集合中找不到值时返回
    nothing
    );它是一个单独的类型
    Nothing
  • missing
    表示“值本身存在,但我们不知道它”(我希望通常只有从外部来源获取数据时,数据中才会出现
    missing
    ,例如,您有患者数据记录,体温缺失(显然存在-只是没有记录);我不认为Base中的任何函数都可以返回它,除非它得到
    缺少
    作为参数);它是一个单独的类型
    缺失
  • NaN
    -只是一个数字数据(与
    缺失
    相反);如果对数值的某些操作的结果返回了
    NaN
    ,它会向用户发出信号;根据我的经验,这是数据中出现
    NaN
    的唯一情况(例如
    0/0
  • undef
    不是您将看到的值,它仅以
    Vector{Int}(undef,10)
    等形式使用,以创建数组而不初始化其值(因此这只是性能优化);只有当您立即想要用一些您计划计算的值初始化数组的元素时,才应该使用它(如果数组的元素类型不是位类型或位类型的并集,则使用
    undef
    将导致
    #undef
    项;对于位类型,使用
    undef
    初始化数组只会产生一些垃圾)
  • 这些是标准规则。现在有一个例外(这是一些其他语言中的典型做法),有时您可能希望使用
    NaN
    来表示集合中的
    缺失
    。这不是推荐的做法,但它有一个好处,您可以在本例中看到:

    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 code时通常认为
    NaN
    是真正的
    NaN
    ,而不是表示
    缺失或
    无内容的占位符

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

    • 如果要定义浮点数数组,但稍后初始化单个元素,出于性能原因,可能需要使用
      unde
      (以避免花费时间将元素设置为一个值,该值将在以后被覆盖):

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

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


    以下是我对这些选项之间差异的看法:




    missing
    用于表示统计意义上的值,即理论上存在但您不知道的值。
    missing
    在精神上(在大多数情况下在行为上)与R中类似。
    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
    相比,它往往用于。这就是为什么与
    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
    的另一个值得注意的用法是在函数参数或对象字段中,不总是为其提供值。这通常在类型系统中表示为
    联合{MeaningfulType,nothing}
    julia> @show println("OK")           # Only side effects
    OK
    println("OK") = nothing
    
    julia> @show findfirst('a', "Hello") # No meaningful result
    findfirst('a', "Hello") = nothing
    
    struct TreeNode
      child1 :: Union{TreeNode, Nothing}
      child2 :: Union{TreeNode, Nothing}
    end
    
    leaf = TreeNode(nothing, nothing)
    
    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> Vector{Float64}(undef, 3)
    3-element Array{Float64,1}:
     6.94567437726575e-310
     6.94569509953624e-310
     6.94567437549977e-310
    
    julia> mutable struct Foo end
    julia> Vector{Foo}(undef, 3)
    3-element Array{Foo,1}:
     #undef
     #undef
     #undef