我应如何在变量中存储“未知”和“缺失”值,同时仍保留“未知”和“缺失”之间的差异?


57

将此视为“学术”问题。我一直想知道不时避免使用NULL,这是我无法提出令人满意的解决方案的一个示例。


假设我将测量值​​存储在有时无法测量(或丢失)的地方。我想将“空”值存储在变量中,同时避免使用NULL。其他时候,该值可能是未知的。因此,有了特定时间范围内的测量值,有关该时间段内测量值的查询可能会返回3种响应:

  • 当时的实际测量值(例如,包括在内的任何数值0
  • “缺失” /“空”值(即已完成测量,并且已知该点为空)。
  • 未知值(即,此时未进行任何测量。它可以为空,但也可以为任何其他值)。

重要说明:

假设您有一个get_measurement()返回“空”,“未知”和类型为“整数”的值的函数。具有数值意味着可以对返回值执行某些运算(乘法,除法...),但是如果对这些运算符使用NULL,则如果不捕获它们,则会使应用程序崩溃。

我希望能够编写代码,例如避免使用NULL检查(伪代码):

>>> value = get_measurement()  # returns `2`
>>> print(value * 2)
4

>>> value = get_measurement()  # returns `Empty()`
>>> print(value * 2)
Empty()

>>> value = get_measurement()  # returns `Unknown()`
>>> print(value * 2)
Unknown()

请注意,所有print语句均未引起异常(因为未使用NULL)。因此,空值和未知值将根据需要传播,并且可以将值实际上是“未知”还是“空”的检查推迟到真正需要之前(例如在某个地方存储/序列化值)。


旁注:我想避免使用NULL的原因主要是脑筋急转弯。如果我想完成工作,我不反对使用NULL,但是我发现避免使用NULL可以使代码在某些情况下更加健壮。


19
为什么要区分“已完成但空值的测量”与“未测量”?实际上,“测量完成但空值”是什么意思?传感器是否无法产生有效值?在这种情况下,这与“未知”有何不同?您将无法及时返回并获得正确的值。
DaveG '18年

3
@DaveG假定获取服务器中的CPU数量。如果服务器已关闭或已被废弃,则该值根本不存在。这将是一个没有任何意义的度量(也许“缺失” /“空”不是最好的术语)。但是“已知”该值是没有意义的。如果服务器存在,但是获取值的过程将崩溃,对其进行测量是有效的,但是失败,将导致“未知”值。
exhuma

2
@exhuma然后将其描述为“不适用”。
文森特

6
出于好奇,您要进行哪种测量,而“空”不只是等于任何标度的零?我看到“未知” /“缺失”很有用,例如,如果没有连接传感器,或者由于某种原因传感器的原始输出为垃圾,但是我能想到的每种情况下的“空”都可以更加一致用0[]或表示{}(分别为标量0,空列表和空映射)。同样,“ missing” /“ unknown”值基本上就是null它的用途-表示那里可能有一个对象,但没有。
Nic Hartley

7
无论您使用哪种解决方案,请务必问自己是否遇到与使您首先要消除NULL的问题类似的问题。

Answers:


85

至少对于功能语言,执行此操作的常用方法是使用区分的联合。然后,这是一个有效int,一个表示“ missing”的值或一个表示“ unknown”的值的值。在F#中,它可能类似于:

type Measurement =
    | Reading of value : int
    | Missing
    | Unknown of value : RawData

Measurement然后Reading,一个值将是一个带有int值的值,一个a Missing或一个Unknown带有原始数据的值value(如果需要)。

但是,如果您使用的语言不支持已区分的并集或同等的并集,那么这种模式对您来说用处不大。因此,您可以在其中使用带有枚举字段的类,该枚举字段指示三个字段中的哪个包含正确的数据。


7
你可以做和类型的面向对象的语言,但有锅炉板公平一点,以使他们的工作stackoverflow.com/questions/3151702/...
JK。

11
“(在非功能语言中)这种模式对您来说用处不大” –在OOP中这是很常见的模式。GOF有这种模式的变体,诸如C ++之类的语言提供了本机结构来对其进行编码。
康拉德·鲁道夫

14
@jk。是的,它们不算在内(我想是的,它们确实如此;由于缺乏安全性,在这种情况下它们非常糟糕)。我的意思是std::variant(及其精神上的前任)。
康拉德·鲁道夫'18

2
@Ewan不,这是说“度量是…或…的数据类型”。
康拉德·鲁道夫

2
@DavidArno即使在没有DU的情况下,在OOP中也有一个“规范”的解决方案,那就是拥有值的超类以及带有有效和无效值的子类。但这可能太过分了(在实践中,似乎大多数代码库都避开了子类多态性,而为此使用了一个标志,如其他答案所示)。
康拉德·鲁道夫

58

如果您还不知道单子是什么,今天将是学习的好日子。我在这里为OO程序员做了一个简短的介绍:

https://ericlippert.com/2013/02/21/monads-part-one/

您的方案是对“也许单子”的一个小扩展,也称为Nullable<T>C#和Optional<T>其他语言。

假设您有一个抽象类型来表示monad:

abstract class Measurement<T> { ... }

然后是三个子类:

final class Unknown<T> : Measurement<T> { ... a singleton ...}
final class Empty<T> : Measurement<T> { ... a singleton ... }
final class Actual<T> : Measurement<T> { ... a wrapper around a T ...}

我们需要一个Bind的实现:

abstract class Measurement<T>
{ 
    public Measurement<R> Bind(Func<T, Measurement<R>> f)
  {
    if (this is Unknown<T>) return Unknown<R>.Singleton;
    if (this is Empty<T>) return Empty<R>.Singleton;
    if (this is Actual<T>) return f(((Actual<T>)this).Value);
    throw ...
  }

由此,您可以编写Bind的简化版本:

public Measurement<R> Bind(Func<A, R> f) 
{
  return this.Bind(a => new Actual<R>(f(a));
}

现在您完成了。你有Measurement<int>手。您想将其加倍:

Measurement<int> m = whatever;
Measurement<int> doubled = m.Bind(a => a * 2);
Measurement<string> asString = m.Bind(a => a.ToString());

并遵循逻辑;如果mEmpty<int>那么asString就是Empty<String>,优秀的。

同样,如果我们有

Measurement<int> First()

Measurement<double> Second(int i);

然后我们可以结合两种测量:

Measurement<double> d = First().Bind(Second);

再一次,如果First()是,Empty<int>dEmpty<double>,依此类推。

关键步骤是使绑定操作正确。认真考虑一下。


4
Monads(感谢)比使用起来容易理解。:)
古兰经'18

11
@leftaroundabout:正是因为我不想陷入那种令人发指的区别。正如原始海报所指出的那样,许多人在处理单子方面缺乏信心。简单操作的术语丰富的类别理论特征不利于建立自信和理解感。
埃里克·利珀特

2
所以您的建议是NullNullable+一些样板代码代替?:)
Eric Duminil

3
@克劳德:您应该阅读我的教程。monad是遵循某些规则的通用类型,并提供将一系列操作绑定在一起的功能,因此,在这种情况下,Measurement<T>此类型为monadic。
埃里克·利珀特

5
@daboross:尽管我同意有状态的单子是引入单子的好方法,但我不认为携带状态是代表单子的事物。我认为您可以将一系列功能绑定在一起这一事实令人信服;有状态性只是实现细节。
埃里克·利珀特

18

我认为在这种情况下,对Null Object Pattern进行更改会很有用:

public class Measurement
{
    private int value;
    private bool isUnknown = false;
    private bool isMissing = false;

    private Measurement() { }
    public Measurement(int value) { this.value = value; }

    public int Value {
        get {
            if (!isUnknown && !isMissing)
            {
                return this.value;
            }
            throw new SomeException("...");
        }                   
    }

    public static readonly Measurement Unknown = new Measurement
    {
        isUnknown = true
    };

    public static readonly Measurement Missing = new Measurement
    {
        isMissing = true
    };
}

您可以将其转换为一个结构,重写Equals / GetHashCode / ToString,从或到添加隐式转换int,如果您想要类似NaN的行为,还可以实现自己的算术运算符,例如。Measurement.Unknown * 2 == Measurement.Unknown

也就是说,C#Nullable<int>实现了所有这些功能,唯一的警告是您无法区分不同类型的nulls。我不是Java的人,但是我的理解是Java OptionalInt很相似,其他语言可能也有自己的表示Optional类型的工具。


6
我见过的这种模式最常见的实现涉及继承。可能存在两个子类的情况:MissingMeasurement和UnknownMeasurement。他们可以实现或覆盖父Measurement类中的方法。+1
格雷格·伯格哈特

2
是不是点空对象模式,你不上无效值失败,而是什么都不做?
克里斯·沃勒特

2
@ChrisWohlert在这种情况下,该对象除Valuegetter 之外实际上没有任何方法,因为您无法将Unknownback 转换为.getter 绝对会失败int。如果度量具有某种SaveToDatabase()方法,那么如果当前对象为空对象(通过与单例的比较或方法重写),那么良好的实现可能不会执行事务。
Maciej Stachowski

3
@MaciejStachowski是的,我不是说它什么也不做,我是说Null Object Pattern不是很好的选择。您的解决方案可能很好,但我不会将其称为Null Object Pattern
克里斯·沃勒特

14

如果从字面上必须使用整数,则只有一种可能的解决方案。使用一些可能的值作为“幻数”,表示“缺失”和“未知”

例如2,147,483,647和2,147,483,646

如果您只需要int来进行“实际”测量,则可以创建更复杂的数据结构

class Measurement {
    public bool IsEmpty;
    public bool IsKnown;
    public int Value {
        get {
            if(!IsEmpty && IsKnown) return _value;
            throw new Exception("NaN");
            }
        }
}

重要说明:

您可以通过重载该类的运算符来满足数学要求

public static Measurement operator+ (Measurement a, Measurement b) {
    if(a.IsEmpty) { return b; }
    ...etc
}

10
@KakturusOption<Option<Int>>
Bergi

5
@Bergi你不可能认为这甚至远程接受..
BlueRaja -丹尼Pflughoeft

8
@ BlueRaja-DannyPflughoeft实际上,它非常适合OP的描述,它也具有嵌套结构。为了被接受,我们当然会引入适当的类型别名(或“ newtype”)-但是type Measurement = Option<Int>对于整数或空读取的结果来说是可以的,Option<Measurement>对于可能已采用或未采用的测量也应这样。
Bergi

7
@arp“ NaN附近的整数”?您能解释一下您的意思吗?说数字“不”不是数字就是这个概念,这似乎有点违反直觉。
Nic Hartley

3
@Nic Hartley在我们的系统中,一组“自然”为最低可能的负整数被保留为NaN。我们使用该空间来编码各种原因,以说明那些字节表示合法数据以外的其他内容的原因。(那是几十年前的事,我可能已经弄不清楚了一些细节,但是如果您想对它进行数学运算,肯定可以将一组位放入一个整数值中,使它抛出NaN。)
arp

11

如果您的变量是浮点数,IEEE754(这是由最先进的处理器和语言支持的浮点数标准)有你的背部:这是一个鲜为人知的功能,但该标准定义不是一个,而是整个家庭NaN(非数字)值,可用于任意应用程序定义的含义。例如,在单精度浮点数中,您有22个可用位,可用于区分2 ^ {22}类型的无效值。

通常,编程接口仅公开其中一个接口(例如Numpy的接口nan)。除了显式的位操作外,我不知道是否有内置的方法来生成其他方法,但这只是编写几个低级例程的问题。(您还需要将它们区分开来,因为根据设计,a == b当其中一个是NaN时,总是返回false。)

使用它们比重塑自己的“幻数”来表示无效数据更好,因为它们可以正确传播并表示无效性:例如,如果您使用某个average()功能而忘记检查是否会冒着脚步的风险您的特殊价值观。

唯一的风险是库无法正确支持它们,因为它们是一个不起眼的功能:例如,序列化库可能会将它们全部“平化”为相同的nan(在大多数情况下看起来都与之相同)。


6

遵循David Arno的回答,您可以执行诸如OOP中的有区别的联合之类的操作,并且可以采用对象功能样式,例如Scala提供的那种,Java 8函数类型或Java FP库(例如VavrFugue),感觉相当很自然地写类似:

var value = Measurement.of(2);
out.println(value.map(x -> x * 2));

var empty = Measurement.empty();
out.println(empty.map(x -> x * 2));

var unknown = Measurement.unknown();
out.println(unknown.map(x -> x * 2));

印刷

Value(4)
Empty()
Unknown()

完整实施为要点。)

FP语言或库提供了其他工具,例如Try(aka Maybe)(一个包含值或错误Either的对象)和(一个包含成功值或失败值的对象),这些工具也可以在此处使用。


2

解决问题的理想解决方案取决于您为什么要关注已知故障和已知不可靠测量之间的差异,以及要支持哪些下游过程。注意,这种情况下的“下游流程”并不排除人工操作人员或其他开发人员。

仅仅提出null的“第二种味道”并不能为下游进程集提供足够的信息来推导合理的行为。

如果您是依靠有关下游代码造成不良行为来源的上下文假设,则将其称为不良架构。

如果您足够了解故障原因和没有已知原因的故障之间的区别,并且该信息将用于通知将来的行为,则应该向下游传达该知识或内联处理。

处理此问题的一些模式:

  • 总和类型
  • 歧视工会
  • 包含代表操作结果的枚举和结果字段的对象或结构
  • 无法通过正常操作获得的魔术字符串或魔术数字
  • 例外,在这种用法是惯用的语言中
  • 意识到区分这两种情况实际上并没有任何价值,仅使用 null

2

如果我关心的是“完成某件事”而不是一个优雅的解决方案,那么快速而又肮脏的hack就是简单地使用字符串“ unknown”,“ missing”和“我的数值的字符串表示形式”,这将是从字符串转换并根据需要使用。比编写本文要快,并且至少在某些情况下完全可以实现。(我现在正在对下注的数目形成一个下注池...)


因提及“完成某件事”而受到赞誉。
烧烤

4
某些人可能会注意到,这与使用NULL有着大多数相同的问题,即它只是从需要NULL检查切换为需要“未知”检查和“缺失”检查,但由于运气不佳而导致运行时崩溃,导致静默数据损坏。不幸的是您忘记检查的唯一指标。即使丢失了NULL检查,其优点是linter可能会抓住它们,但这会丢失。不过,它确实在“未知”和“缺失”之间添加了区别,因此它在那里击败了NULL ...
8bittree

2

要点是如果问题似乎是“我如何从返回单个int的方法中返回两条不相关的信息?我从不希望检查我的返回值,并且null不好,请不要使用它们。”

让我们看看您想通过什么。您正在通过一个int或一个非int的基本原理来解释为什么不能给出int。这个问题断言只有两个原因,但是任何曾经做过枚举的人都知道任何列表都会增长。指定其他原理的范围是有意义的。

最初,然后看来,这可能是引发异常的好案例。

当您想告诉调用者一些特殊的东西(不是返回类型)时,异常通常是合适的系统:异常不仅用于错误状态,还允许您返回很多上下文和理由来解释为什么您可以今天不是。

这是允许您返回保证有效的整数的唯一系统,并保证每个采用整数的int运算符和方法都可以接受此方法的返回值,而无需检查无效值(例如null或magic值)。

但是,正如其名称所暗示的那样,例外实际上仅是一种有效的解决方案,这是一种例外情况,而不是正常的业务过程。

而try / catch和handler和null检查一样,都是最初的目的。

如果呼叫者不包含try / catch,则呼叫者的呼叫者必须如此,依此类推。


天真的第二遍是说“这是一个测量。不可能进行负距离测量”。因此,对于某些测量值Y,您可以只为

  • -1 =未知,
  • -2 =无法测量,
  • -3 =拒绝回答,
  • -4 =已知但机密,
  • -5 =取决于月相,请参见表5a,
  • -6 =三维,尺寸以标题给出,
  • -7 =文件系统读取错误,
  • -8 =保留以备将来使用,
  • -9 =平方/立方,因此Y与X相同,
  • -10 =是监视器屏幕,因此不使用X,Y测量:使用X作为屏幕对角线,
  • -11 =将尺寸写在收据的背面,被洗得难以辨认,但我认为它是5或17
  • -12 = ...您明白了。

这是在许多旧的C系统中甚至在对int有真正约束的现代系统中完成的方式,您不能将其包装到某种类型的struct或monad上。

如果测量结果可能为负,则只需将数据类型设置为更大(例如,long int),并且魔术值大于int的范围,并且理想情况下应从将在调试器中清楚显示的某个值开始。

有充分的理由将它们作为一个单独的变量,而不是仅仅具有幻数。例如,严格的键入,可维护性和符合期望。


然后,在第三次尝试中,我们将具有非整数值的正常业务活动作为案例。例如,如果这些值的集合可能包含多个非整数条目。这意味着异常处理程序可能是错误的方法。

在这种情况下,对于通过int和其原理的结构来说,这是一个很好的例子。同样,此原理可以像上面一样是一个const,但是您不必将它们都保存在同一int中,而是将它们存储为结构的不同部分。最初,我们有一个规则,即如果设置了基本原理,则不会设置int。但是我们不再局限于这个规则。如果需要,我们也可以提供有效数字的依据。

无论哪种方式,每次调用它时,您仍然都需要样板,以测试基本原理以查看int是否有效,然后在基本原理允许的情况下退出并使用int部分。

这是您需要调查“请勿使用null”背后的原因的地方。

像异常一样,null表示异常状态。

如果调用者正在调用此方法并完全忽略结构的“ rationale”部分,期望一个没有任何错误处理的数字,并且它得到零,那么它将把零作为数字来处理,这是错误的。如果它得到一个幻数,它将把它当作一个数字,这是错误的。但是,如果它为null,它就会倒塌,这真是该死的。

因此,每次调用此方法时,都必须检查其返回值,但是您将处理无效值(带内或带外),尝试/捕获,检查“合理”组件的结构,检查int输入一个魔术数字,或检查一个int是否为空...

处理可能包含无效int和“我的狗吃了这个度量”等基本原理的输出的乘法的另一种方法是,对该结构的乘法运算符进行重载。

...然后重载应用程序中可能会应用于此数据的所有其他运算符。

...然后重载可能采用int的所有方法。

...而且所有这些重载将仍然需要包含对无效int的检查,只是为了使您可以将此方法的返回类型视为在调用它时始终是有效int。

因此,原始前提在各种方面都是错误的:

  1. 如果您有无效值,那么您将在处理这些值的代码中随时避免检查那些无效值。
  2. 如果返回的不是int,则不返回int,因此不能将其视为int。运算符重载让您假装,但这只是假装。
  3. 具有魔术数字(包括NULL,NAN,Inf ...)的int不再是真正的int,它是穷人的结构。
  4. 避免使用null不会使代码更健壮,它只会隐藏int问题,或者将它们移到复杂的异常处理结构中。

1

我不明白您提出问题的前提,但这是面值答案。对于“缺失”或“空”,您可以这样做math.nan(而不是数字)。您可以在上执行任何数学运算math.nan并将其保留math.nan

您可以使用None(Python的null)表示未知值。无论如何,您都不应该操纵一个未知值,并且某些语言(Python不是其中的一种)具有特殊的null运算符,因此仅在该值非null时才执行该操作,否则该值保持为null。

其他语言也有保护子句(例如Swift或Ruby),而Ruby有条件提早返回。

我已经看到在Python中以几种不同的方式解决了这个问题:

  • 使用包装器数据结构,因为数值信息通常与实体有关并且具有测量时间。包装器可以覆盖魔术方法,例如__mult__这样,当出现Unknown或Missing值时,不会引发异常。脾气暴躁和大熊猫可能具有这种能力。
  • 带有前哨值(例如您的Unknown或-1 / -2)和if语句
  • 用一个单独的布尔标志
  • 使用惰性数据结构-您的函数对结构执行一些操作,然后返回,需要实际结果的最外面的函数评估惰性数据结构
  • 具有惰性的操作流水线-与上一个操作流水线类似,但是该操作可用于一组数据或数据库

1

值如何存储在内存中取决于语言和实现细节。我认为您的意思是对象应如何表现给程序员。(这是我阅读问题的方式,告诉我是否错。)

您已经在问题中提出了一个答案:使用您自己的类,该类接受任何数学运算并返回自身而不会引发异常。您说要这样做是因为要避免空检查。

解决方案1:避免空检查

Missing可以表示为math.nan
Unknown可以表示为None

如果您有多个值,则filter()只能将操作应用于不是Unknown或的Missing值,或者要对该函数忽略的任何值。

我无法想象这样一种情况,您需要对作用于单个标量的函数进行空检查。在这种情况下,最好强制执行空检查。


解决方案2:使用装饰器捕获异常

在这种情况下,Missing可能会上升,MissingException并且在对其执行操作时Unknown可能会上升UnknownException

@suppressUnknown(value=Unknown) # if an UnknownException is raised, return this value instead
@suppressMissing(value=Missing)
def sigmoid(value):
    ...

这种方法的优点是性能MissingUnknown当你明确要求对它们进行压制只是抑制。另一个优点是该方法具有自我记录功能:每个功能都可以表明它是否期望未知或缺失以及功能如何。

当您调用某个函数并不期望Missing会导致Missing丢失时,该函数将立即引发,向您显示错误发生的确切位置,而不是默默地失败并在调用链中传播Missing。未知也是如此。

sigmoid仍然可以调用sin,即使它不希望使用Missingor 也不希望这样做Unknown,因为sigmoid的装饰器将捕获该异常。


1
想知道发布同一问题的两个答案有什么意义(这是您先前的答案,有什么问题吗?)
t

@gnat这个答案提供了为什么不应该按照作者显示的方式进行推理的原因,而我也不想经历将两个答案与不同想法结合在一起的麻烦-写两个可以独立阅读的答案会更容易。我不明白您为什么这么在乎别人的无害推理。
noɥʇʎԀʎzɐɹƆ

0

假定获取服务器中的CPU数量。如果服务器已关闭或已被废弃,则该值根本不存在。这将是没有任何意义的度量(也许“遗漏” /“空”不是最好的术语)。但是“已知”该值是没有意义的。如果服务器存在,但是获取值的过程将崩溃,对其进行测量是有效的,但是失败,将导致“未知”值。

这两个听起来都像是错误情况,所以我认为最好的选择是get_measurement()立即将这两个都立即抛出为异常(例如DataSourceUnavailableExceptionSpectacularFailureToGetDataException)。然后,如果出现这些问题中的任何一个,则数据收集代码可以立即对其做出反应(例如,在后一种情况下通过再次尝试),并且get_measurement()int在可以成功从数据中获取数据的情况下才返回。来源-并且您知道int合法。

如果您的情况不支持异常或无法充分利用它们,那么一个不错的选择是使用错误代码,也许通过单独的输出返回get_measurement()。这是C语言中的惯用模式,其中实际输出存储在输入指针中,并且错误代码作为返回值传回。


0

给出的答案很好,但仍不能反映空值和未知值之间的层次关系。

  • 最高的未知
  • 然后,在使用值之前,必须先清除为
  • 最后是要计算的

丑陋的(由于其失败的抽象),但完全可操作的(在Java中):

Optional<Optional<Integer>> unknowableValue;

unknowableValue.ifPresent(emptiableValue -> ...);
Optional<Integer> emptiableValue = unknowableValue.orElse(Optional.empty());

emptiableValue.ifPresent(value -> ...);
int value = emptiableValue.orElse(0);

在这里,具有良好类型系统的功能语言会更好。

事实上:空/失踪不明*非价值观似乎有些进程状态,一些生产流水线,而一部分。类似于Excel的电子表格单元格,其中的公式引用了其他单元格。有人会想到可能会存储上下文lambda。更改单元格将重新评估所有递归相关的单元格。

在这种情况下,int供应商将获得int值。空值将使int供应商抛出空异常或评估为空(递归向上)。您的主要公式将连接所有值,并且可能还会返回一个空值(值/异常)。未知值将通过引发异常来禁用评估。

值可能是可观察到的,例如java绑定属性,可在更改时通知侦听器。

简而言之:需要带有其他状态为空且未知的其他状态的值的重复模式似乎表明,像绑定属性数据模型这样的电子表格越多越好。


0

是的,某些语言中存在多种不同的NA类型的概念;在统计意义上更是如此,在统计意义上更有意义(即,“ 随机遗失”,“随机遗失完全”和“随机遗失”之间的巨大区别)。

  • 如果我们仅测量小部件的长度,那么区分“传感器故障”,“断电”或“网络故障”并不是至关重要的(尽管“数值溢出”确实传达了信息)

  • 但是在例如数据挖掘或调查中,向受访者询问他们的收入或艾滋病毒状况,“未知”的结果与“拒绝回答”是截然不同的,您可以看到我们先前关于如何估算后者的假设会趋于与前者不同 因此,SAS等语言支持多种不同的NA类型。R语言没有,但是用户经常不得不对此进行破解。管道中不同点的NA可以用来表示非常不同的事物。

  • 在某些情况下,单个条目也会有多个NA变量(“多个插补”)。例如:如果我不知道一个人的年龄,邮政编码,受教育程度或收入,则很难估算他们的收入。

至于您如何用不支持它们的通用语言表示不同的NA类型,通常人们会乱砍诸如浮点NaN(需要转换整数),枚举或前哨(例如999或-1000)这样的整数或整数。分类值。通常没有一个很明确的答案,对不起。


0

R具有内置的缺失值支持。https://medium.com/coinmonks/dealing-with-missing-data-using-r-3ae428da2d17

编辑:因为我被否决了,我将解释一下。

如果您要处理统计数据,我建议您使用诸如R之类的统计语言,因为R是由统计人员为统计人员编写的。价值观缺失是一个很大的话题,以至于他们会教你整个学期。而且有很多关于遗漏价值的大书。

但是,您可以标记丢失的数据,例如点或“缺失”之类的内容。在R中,您可以定义缺失的含义。您不需要转换它们。

定义缺失值的通常方法是将它们标记为NA

x <- c(1, 2, NA, 4, "")

然后,您可以查看缺少的值;

is.na(x)

然后结果将是:

FALSE FALSE  TRUE FALSE FALSE

如您所见,""它并不缺少。您可以威胁""未知。和NA失踪。


@Hulk,还有哪些其他功能语言支持缺失值?即使它们支持缺失值,我也确保您无法仅用一行代码用统计方法填充它们。
ilhan

-1

是否有理由*不能更改操作员的功能?

大多数答案都涉及某种查找值,但在这种情况下,修改数学运算符可能会更容易。

然后,您将能够有类似的empty()/ unknown()在你的整个项目的功能。


4
这意味着您将不得不使所有操作员超负荷运行
管道
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.