45 | Datasets |

Especially in larger organizations, computing often centers around dealing with large amounts of structured data. The Wolfram Language has a very powerful way to deal with structured data, using what it calls

*datasets*.Create a simple dataset that can be viewed as having 2 rows and 3 columns:

In[1]:= |

Out[1]= |

The Wolfram Language displays most datasets in tabular form. You can extract parts from datasets just like you would from associations.

In[2]:= |

Out[2]= |

You can first extract the whole “b row”, then get the “z” element of the result:

In[3]:= |

Out[3]= |

You can also just get the whole “b row” of the dataset. The result is a new dataset, which for ease of reading happens to be displayed in this case as a column.

Generate a new dataset from the “b row” of the original dataset:

In[4]:= |

Out[4]= |

Here is the dataset that corresponds to the “z column” for all “rows”.

Generate a dataset consisting of the “z column” for all rows:

In[5]:= |

Out[5]= |

Extracting parts of datasets is just the beginning. Anywhere you can ask for a part you can also give a function that will be applied to all parts at that level.

In[6]:= |

Out[6]= |

If we use f instead of Total, we can see what’s going on: the function is being applied to each of the “row” associations.

Apply the function f to each row:

In[7]:= |

Out[7]= |

Apply a function that adds the x and z elements of each association:

In[8]:= |

Out[8]= |

In[9]:= |

Out[9]= |

You can give a function to apply to all rows too.

This extracts the value of each “z column”, then applies f to the association of results:

In[10]:= |

Out[10]= |

Apply f to the totals of all columns:

In[11]:= |

Out[11]= |

In[12]:= |

Out[12]= |

You can always “chain” queries, for example first finding the totals for all rows, then picking out the result for the “b row”.

Find totals for all rows, then pick out the total for the “b row”:

In[13]:= |

Out[13]= |

It’s equivalent to this:

In[14]:= |

Out[14]= |

Particularly when one’s dealing with large datasets, it’s common to want to select parts based on a criterion. The

*operator form*of Select provides a very convenient way to do this.In[15]:= |

Out[15]= |

In[16]:= |

Out[16]= |

The operator form of Select is a function which can be applied to actually perform the Select operation.

Make a dataset by selecting only rows whose “z column” is greater than 5:

In[17]:= |

Out[17]= |

For each row, select columns whose values are greater than 5, leaving a ragged structure:

In[18]:= |

Out[18]= |

In[19]:= |

Out[19]= |

Many Wolfram Language functions have operator forms.

In[20]:= |

Out[20]= |

SortBy has an operator form:

In[21]:= |

Out[21]= |

Sort rows according to the value of the difference of the x and y columns:

In[22]:= |

Out[22]= |

Sort the rows, and find the total of all columns:

In[23]:= |

Out[23]= |

Sometimes you want to apply a function to each element in the dataset.

Apply f to each element in the dataset:

In[24]:= |

Out[24]= |

Sort the rows before totaling the squares of their elements:

In[25]:= |

Out[25]= |

Datasets can contain arbitrary mixtures of lists and associations. Here’s a dataset that can be thought of as a

*list of records*with named*fields*.A dataset formed from a list of associations:

In[26]:= |

Out[26]= |

In[27]:= |

Out[27]= |

Now that we’ve seen some simple examples, it’s time to look at something slightly more realistic. Let’s import a dataset giving properties of planets and moons. The dataset has a hierarchical structure, with each planet having a mass and radius of its own, and then also having a collection of moons, each of which have their own properties. This general structure is extremely common in practice (think students and grades, customers and orders, etc.).

In[28]:= |

Out[28]= |

In[29]:= |

Out[29]= |

In[30]:= |

Out[30]= |

If we ask about the moons of Mars, we get a dataset, which we can then query further.

Get a dataset about the moons of Mars:

In[31]:= |

Out[31]= |

“Drill down” to make a table of radii of all the moons of Mars:

In[32]:= |

Out[32]= |

We can do computations about the moons of all planets. First, let’s just find out how many moons are listed for each planet.

Make a dataset of the number of moons listed for each planet:

In[33]:= |

Out[33]= |

Find the total mass of all moons for each planet:

In[34]:= |

Out[34]= |

Get the same result, but only for planets with more than 10 moons:

In[35]:= |

Out[35]= |

In[36]:= |

Out[36]= |

Get a dataset with moons that are more than 1% of the mass of the Earth.

For all moons, select ones whose mass is greater than 0.01 times the mass of the Earth:

In[37]:= |

Out[37]= |

Get the list of keys (i.e. moon names) in the resulting association for each planet:

In[38]:= |

Out[38]= |

In[39]:= |

Out[39]= |

In[40]:= |

Out[40]= |

Here’s the whole computation in one line:

In[41]:= |

Out[41]= |

Make number line plots of the logarithms of masses for moons of each planet:

In[42]:= |

Out[42]= |

As a final example, let’s make a word cloud of names of moons, sized according to the masses of the moons. To do this, we need a single association that associates the name of each moon with its mass.

In[43]:= |

Out[43]= |

The function Association combines associations:

In[44]:= |

Out[44]= |

In[45]:= |

Out[45]= |

For what it does, the code here is surprisingly simple. But we can make it slightly more streamlined by using @* or /*.

We’ve seen before that we can write something like f[g[x]] as f@g@x or x//g//f. We can also write it f[g[#]]&[x]. But what about f[g[#]]&? Is there a short way to write this? The answer is that there is, in terms of the

*function composition operators*@* and /*.In[46]:= |

Out[46]= |

In[47]:= |

Out[47]= |

Here’s the previous code rewritten using composition @*:

In[48]:= |

Out[48]= |

In[49]:= |

Out[49]= |

Vocabulary

Dataset[data] | a dataset | |

Normal[dataset] | convert a dataset to normal lists and associations | |

Catenate[{assoc_{1},...}] | catenate associations, combining their elements | |

f@*g | composition of functions (f[g[x]] when applied to x) | |

f/*g | right composition (g[f[x]] when applied to x) |

**Note:**These exercises use the dataset planets=CloudGet["http://wolfr.am/7FxLgPm5"].

45.1Make a word cloud of the planets, with weights determined by their number of moons. »

45.2Make a bar chart of the number of moons for each planet. »

45.3Make a dataset of the masses of the planets, sorted by their number of moons. »

45.5Make a dataset of masses of planets, where the planets are sorted by the largest mass of their moons. »

45.6Make a dataset of the median mass of all moons for each planet. »

45.7For each planet, make a list of moons larger in mass than 0.0001 Earth masses. »

45.8Make a word cloud of countries in Central America, with the names of countries proportional to the lengths of the Wikipedia article about them. »

What kinds of data can datasets contain?

Any kinds. Not just numbers and text but also images, graphs and lots more. There’s no need for all elements of a particular row or column to be the same type.

Yes. SemanticImport is often a good way to do it.

What are databases and how do they relate to Dataset?

Databases are a traditional way to store structured data in a computer system. Databases are often set up to allow both reading and writing of data. Dataset is a way to represent data that might be stored in a database so that it’s easy to manipulate with the Wolfram Language.

How does data in Dataset compare to data in an SQL (relational) database?

SQL databases are strictly based on tables of data arranged in rows and columns of particular types, with additional data linked in through “foreign keys”. Dataset can have any mixture of types of data, with any number of levels of nesting, and any hierarchical structure, somewhat more analogous to a NoSQL database, but with additional operations made possible by the symbolic nature of the language.

Tech Notes

- Dataset supports a new kind of
*symbolic database structure*which generalizes both*relational*and*hierarchical databases*. - Dataset has many additional mechanisms and capabilities that we haven’t discussed.
- Everything that can be done with queries on datasets can also be done by using functions like Map and Apply on underlying lists and association—but it’s typically much simpler with dataset queries.
- You can connect the Wolfram Language directly to SQL databases—and do queries with SQL syntax—using DatabaseLink.