4
$\begingroup$

DeleteMissing doesn't work in Association tables:

Dataset[{<|"a" -> 1, "b" -> Missing[]|>, <|"a" -> 2, 
     "b" -> 3|>}][DeleteMissing,"b"] // Normal

{Missing[], 3}

Whereas DeleteCases[_Missing] works, as does [All, "b"][DeleteMissing] and [{DeleteMissing, DeleteCases[_Missing]}, "b"] --> {{3},{3}}

$\endgroup$
2
  • 1
    $\begingroup$ I think you need a level specification there to make it work: for instance, try Dataset[...][DeleteMissing[#, 1, 2] &]. $\endgroup$
    – MarcoB
    Commented Oct 7, 2015 at 20:04
  • 1
    $\begingroup$ I originally (and somewhat tentatively) voted to close this item as a duplicate of (59595). However, @alancalvitti pointed out the additional question about operators of the form {...}, so I have retracted my Close vote. $\endgroup$
    – WReach
    Commented Oct 8, 2015 at 3:22

1 Answer 1

5
$\begingroup$

The documentation for Dataset (and Query) indicates that DeleteMissing is a descending operator. This means that it is applied to the original list before the "b" suboperator is applied. Since none of the original list elements have the head Missing, the descending DeleteMissing is effectively a null operation in this case.

Original Case - DeleteMissing

What we need to do is convert the operator into an ascending operator. We start with the original case:

ds = Dataset[{<|"a"->1,"b"->Missing[]|>,<|"a"->2,"b"->3|>}];

ds[DeleteMissing, "b"] // Normal
(* {Missing[], 3} *)

Any descending operator can be converted into an ascending operator by wrapping it with Query[...]:

ds[Query[DeleteMissing], "b"] // Normal
(* {3} *)

Pure functions are also treated as ascending:

ds[DeleteMissing[#]&, "b"] // Normal
(* {3} *)

Composing a descending operator with a preceding ascending operator will also convert it -- #& is a terse choice:

ds[#& /* DeleteMissing, "b"] // Normal
(* {3} *)

As noted in the question, performing a second query will allow the descending operator to be applied in descending fashion, but after the operations in the first query are complete:

ds[All, "b"][DeleteMissing] // Normal
(* {3} *)

DeleteCases

DeleteCases is an ascending operator, which is why it gives the desired result:

ds[DeleteCases[_Missing], "b"] // Normal
(* {3} *)

{DeleteMissing}

The {...} operator is (usually) an ascending operator. Thus, any suboperators it contains are applied after the other query descending operators:

ds[{DeleteMissing}, "b"] // Normal
(* {{3}} *)

While tangential to the present question, note that in some circumstances the {...} operator can be considered a descending operator. This happens if and only if the contained suboperators are all simple part operators. For example:

{<|"a" -> 11|>, <|"a" -> 22|>, <|"a" -> 33|> } // Query[{2, 3}, "a"]
(* {22, 33} *)

This exception is made because even though part operators alter the descending structure, they do so in a well-defined fashion which is akin to filtering. The query machinery can still infer useful properties about the result of such part filtering. DeleteMissing is not so benign, and can change the shape of the data in ways that are more difficult to reason about up front. The difference is that a list of part operators generates a result of fixed cardinality, whereas {DeleteMissing} generates a result of varying cardinality. So {DeleteMissing} does not qualify for the exception and is interpreted as an ascending operator.

$\endgroup$
2
  • $\begingroup$ Given your explanations, I don't see why does this works as intended [{DeleteMissing, DeleteCases[_Missing]}, "b"]. Is it b/c it's automatically wrapped in Query? $\endgroup$ Commented Oct 8, 2015 at 2:11
  • $\begingroup$ The {...} operator is (usually) an ascending operator. I have updated the response to discuss this case, as well as DeleteCases. $\endgroup$
    – WReach
    Commented Oct 8, 2015 at 3:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.