Message Boards Message Boards

Saving data directly in notebooks

Posted 7 years ago

Do you ever wish you could save data directly into a notebook instead of having to export it? I sometimes do things like

mydata = {1,2,3, ...};

just to be able to use this data next time I open the notebook. This could be data I pasted from the web, or something that just took several minutes to generate.

But saving it like this is inconvenient, and often takes up a lot of space.

Today I published a blog post about a better method that I have been using recently. I thought people here may be interested:

The idea is to use first Compress the data, then use Interpretation to create a compact display for it. Here's a small function that packages all of this up:

ClearAll[SaveToCell]

SaveToCell::usage =
    "SaveToCell[variable] creates an input cell that reassigns the current value of variable.\n" <>
    "SaveToCell[variables, display] shows 'display' on the right-hand-side of the assignment.";

SetAttributes[SaveToCell, HoldFirst]
SaveToCell[var_, name : Except[_?OptionQ] : "data", opt : OptionsPattern[]] :=
    With[{data = Compress[var],
      panel = ToBoxes@Tooltip[Panel[name, FrameMargins -> Small], DateString[]]},
      CellPrint@Cell[
        BoxData@RowBox[{
          MakeBoxes[var],
          "=",
          InterpretationBox[panel, Uncompress[data]],
          ";"
        }],
        "Input",
        (* prevent deletion by Cell > Delete All Output: *)
        GeneratedCell -> False,
        (* CellLabel is special: last occrrence takes precedence, so it comes before opt: *)
        CellLabel -> "(saved)",
        opt,
        CellLabelAutoDelete -> False
      ]
    ]

If you have your data in the variable var, simply run SaveToCell[var], which will create an input cell that re-assign the value of var. It looks like this:

enter image description here

We can also customize the display:

var = Range[1000];
SaveToCell[var, Short[var]]

enter image description here

Hovering the display will show the date when the data was saved.

SaveToCell also takes arbitrary Cell options, and passes them down to the generated cell. Something strange I observed while writing this function is that with some options, such as CellLabel, it is not the first but the last occurrence of the option that takes precedence. Does anyone know why?

We can use this functionality to change the cell style, add a different label, or to protect the cell against accidental deletion: SaveToCell[var, Deletable -> False].

I have been using this little function for a while, and I hope that others will find it useful too.

Do be careful though: notebooks are not designed for storing large data. I would avoid storing data as large as several tens of megabytes within notebooks.

POSTED BY: Szabolcs Horvát
8 Replies
Posted 7 years ago

Very cool Szabolcs, much better than pasting large matrices in an input cell and walling it off within a Section.

POSTED BY: Updating Name

About CellLabel... the same thing happens with CellTags. I don't know of other options like this that behave differently (i.e. last option takes precedence). I have already reported this issue to Wolfram Technical Support. If you know of more, then please let me know and I'll update the report.

POSTED BY: Kevin Daily
Posted 7 years ago

I wonder if a user can make his own iconography thingy like the one in Out[3] in https://reference.wolfram.com/language/ref/TimeSeries.html? It has more features like myFunctionHead[ ] around the icon and the + button, etc.

POSTED BY: Data Computist

Yes. They are called “summary boxes”. See here:

See István's answer on how to create summary boxes that are just like the builtin ones. This requires using some undocumented things (but I don't expect that to be problematic in this case). See my answer on how to imitate them by using only documented constructs.

POSTED BY: Szabolcs Horvát

I forgot to say that when you create summary boxes, you must be extremely careful not to introduce evaluation leaks. Here's a naive way to do it, which will then cause problems:

ClearAll[date]
date /: MakeBoxes[expr : date[{y_, m_, d_}], form : StandardForm | TraditionalForm] :=
 With[{boxes = 
    ToBoxes[Panel@Grid[{{"year:", y}, {"month:", m}, {"day:", d}}], form]},
  InterpretationBox[boxes, expr]
  ]

enter image description here

All good so far.

Now let's make a function that creates a date:

make2017Date[{m_, d_}] := date[{2017, m, d}]

And see its definition:

enter image description here

Still looking good. But now define:

m = 12;

enter image description here

Oops! Evaluation leak!

Something much worse could happen too. What if m contains code, like m := Print["Boo!"]?

The simple way to fix it is to put checks on your values:

date /: MakeBoxes[expr : date[{y_Integer, m_Integer, d_Integer}], ...] := ...
POSTED BY: Szabolcs Horvát

I think that this functionality should be built into Wolfram Language. It is similar to static variables or #defines in c and other languages.

enter image description here - Congratulations! This post is now a Staff Pick! Thank you for your wonderful contributions. Please, keep them coming!

POSTED BY: Moderation Team

Very neat! Thanks for sharing!

POSTED BY: Sander Huisman
Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard

Group Abstract Group Abstract