13
$\begingroup$

NB: The problem illustrated below is truly ubiquitous. I hope the examples given below are sufficiently different to demonstrate this fact, and to discourage answers that hinge on the details of the examples.


Example 1:

columnSpecs = Module[
   {
      numberOfRows = 4
    , columnNames = {"a", "b", "c"}
    , numberOfColumns
    , columns
   }

   , numberOfColumns = Length[columnNames]
   ; SeedRandom[0]
   ; columns = Partition[ RandomSample[ Range[numberOfRows * numberOfColumns]], numberOfRows]
   ; AssociationThread[columnNames, columns]
];   


ugly = Dataset[columnSpecs][Transpose];
nice = Dataset[ Transpose[columnSpecs, AllowedHeads -> All]];

Now, the datasets ugly and nice have exactly the same content:

Normal[ugly] == Normal[nice]
(* True *)

but they "look" different:

{ugly, nice}

Mathematica graphics

(ugly: "What does nice have that I don't?" Life is so unfair.)


Example 2:

In this case, the RHS expressions in the definitions of ugly and nice have exactly the same form, but differ in content.

ugly = Dataset[{<|"a" -> 11, "b" ->  2 , "c" -> 12|>}];
nice = Dataset[{<|"a" -> 11, "b" -> "2", "c" -> 12|>}];

{ugly, nice}

Mathematica graphics


Bottom line: Datasets get displayed inconsistently.


How can one canonicalize "ugly" Datasets so that they look "nice"?

As pointed out at the beginning of this post, in my experience, "ugly" Datasets arise in a very wide variety of ways, and therefore it would be a fool's errand to attempt to solve this problem at the point of generation of these "ugly" Datasets (there would be too many do's and don't's to remember). For instance, one could avoid the "ugly" Dataset of Example 1 by using Dataset[columnSpecs][Transpose[#, AllowedHeads->All]&] instead of Dataset[columnSpecs][Transpose], but this solution is very specific to the particularities of Transpose, and is therefore useless for the case shown in Example 2.

Hence, I'm much more interested in ways to canonicalize (i.e. fix) the "ugly" Datasets after-the-fact (the "remedial" strategy) than in ways to avoid them in the first place (the "preventive" strategy). One can hope the remedial strategy would be the one more likely to yield the more generally applicable solution.

$\endgroup$
2

1 Answer 1

15
$\begingroup$

The reason for the difference between ugly and nice is in the (hidden) data type that Dataset assigns them:

ugly = Dataset[{<|"a" -> 11, "b" -> 2, "c" -> 12|>}];
nice = Dataset[{<|"a" -> 11, "b" -> "2", "c" -> 12|>}];
<< Dataset`;
<< TypeSystem`;

In[43]:= ugly // GetType

(* Vector[Assoc[Atom[Enumeration["a", "b", "c"]], Atom[Integer], 3], 1] *)

In[44]:= nice // GetType

(* Vector[Struct[{"a", "b", "c"}, {Atom[Integer], Atom[String], Atom[Integer]}], 1] *)

Dataset bases its typesetting on these types. In this case, there isn't a good typesetting rule implemented for the Vector[Assoc[Atom[Enumeration[...],...]]] type (this is a bug that'll be fixed in a future version of Mathematica).

In order to bypass this, we can assign ugly a type manually, via Dataset's hidden second argument, so:

notugly = Dataset[{<|"a" -> 11, "b" -> 2, "c" -> 12|>}, 
          Vector[Struct[{"a", "b", "c"}, {Atom[Integer], Atom[Integer], Atom[Integer]}], 1]]

This is possible because the same underlying data can conform to several different types, one of which could have a nice typesetting rule, as notugly does:

non-ugly typesetting

$\endgroup$
1
  • $\begingroup$ The display situation is still rather a mess. It seems Mma is not exploiting structure it is aware of. To illustrate in 12.1, Dataset@GroupBy[ Range[6], {If[PrimeQ@#, "yes", "no"] &, If[EvenQ@#, "even", "odd"] &}]` displays hierchically, but transposing it gives a nice 2-way display, as does transposing that last result. $\endgroup$
    – Alan
    Commented May 22, 2020 at 19:51

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.