mirror of
https://github.com/johnkerl/miller.git
synced 2026-07-17 16:38:54 +00:00
More miller-6 mkdocs porting from sphinx (#617)
* More mkdocs porting * mkdocs-ify http/external links * move README.md up one level * Further sharpen code-sample CSS * fix last internal-ref links
This commit is contained in:
parent
11eac853d2
commit
fa3ee05822
141 changed files with 4449 additions and 4352 deletions
42
docs6b/README.md
Normal file
42
docs6b/README.md
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# Miller docs
|
||||
|
||||
## Why use Mkdocs
|
||||
|
||||
* Connects to https://miller.readthedocs.io so people can get their docmods onto the web instead of the self-hosted https://johnkerl.org/miller/doc. Thanks to @pabloab for the great advice!
|
||||
* More standard look and feel -- lots of people use readthedocs for other things so this should feel familiar.
|
||||
* We get a Search feature for free.
|
||||
* Mkdocs vs Sphinx: these are similar tools, but I find that I more easily get better desktop+mobile formatting using Mkdocs.
|
||||
|
||||
## Contributing
|
||||
|
||||
* You need `pip install mkdocs` (or `pip3 install mkdocs`).
|
||||
* The docs include lots of live code examples which will be invoked using `mlr` which must be somewhere in your `$PATH`.
|
||||
* Clone https://github.com/johnkerl/miller and cd into `docs/` within your clone.
|
||||
* Quick-editing loop:
|
||||
* In one terminal, cd to this directory and leave `mkdocs serve` running.
|
||||
* In another terminal, cd to the `docs` subdirectory and edit `*.md.in`.
|
||||
* In your browser, visit http://127.0.0.1:8000
|
||||
* Alternate editing loop:
|
||||
* Leave one terminal open as a place you will run `mkdocs build`
|
||||
* In one terminal, cd to the `docs` subdirectory and edit `*.md.in`.
|
||||
* In the first terminal, run `mkdocs build` which will populate the `site` directory.
|
||||
* In your browser, visit `file:///your/path/to/miller/docs/site/index.html`
|
||||
* Link-checking:
|
||||
* `sudo pip3 install git+https://github.com/linkchecker/linkchecker.git`
|
||||
* `cd site` and `linkchecker .`
|
||||
* Submitting:
|
||||
* `git add` your modified files, `git commit`, `git push`, and submit a PR at https://github.com/johnkerl/miller.
|
||||
|
||||
## Notes
|
||||
|
||||
* CSS:
|
||||
* I used the Mkdocs Readthedocs theme which I like a lot. I customized `docs/extra.css` for Miller coloring/branding.
|
||||
* Live code:
|
||||
* I didn't find a way to include non-Python live-code examples within Mkdocs so I adapted the pre-Mkdocs Miller-doc strategy which is to have a generator script read a template file (here, `foo.md.in`), run the marked lines, and generate the output file (`foo.md`). This is `genmds`.
|
||||
* Edit the `*.md.in` files, not `*.md` directly.
|
||||
* Within the `*.md.in` files are lines like `GENMD_RUN_COMMAND`. These will be run, and their output included, by `genmds` which calls the `genmds` script for you.
|
||||
* readthedocs:
|
||||
* https://readthedocs.org/
|
||||
* https://readthedocs.org/projects/miller/
|
||||
* https://readthedocs.org/projects/miller/builds/
|
||||
* https://miller.readthedocs.io/en/latest/
|
||||
|
|
@ -1,2 +1 @@
|
|||
map \d :w<C-m>:!clear;build-one %<C-m>
|
||||
map \f :w<C-m>:!clear;make html<C-m>
|
||||
|
|
|
|||
|
|
@ -5,23 +5,28 @@
|
|||
|
||||
You can install Miller for various platforms as follows:
|
||||
|
||||
* Linux: ``yum install miller`` or ``apt-get install miller`` depending on your flavor of Linux
|
||||
* MacOS: ``brew install miller`` or ``port install miller`` depending on your preference of [Homebrew](https://brew.sh>`_ or `MacPorts <https://macports.org).
|
||||
* Windows: ``choco install miller`` using [Chocolatey](https://chocolatey.org).
|
||||
* You can get latest builds for Linux, MacOS, and Windows by visiting https://github.com/johnkerl/miller/actions, selecting the latest build, and clicking _Artifacts_. (These are retained for 5 days after each commit.)
|
||||
* Linux: `yum install miller` or `apt-get install miller` depending on your flavor of Linux
|
||||
* MacOS: `brew install miller` or `port install miller` depending on your preference of [Homebrew](https://brew.sh) or [MacPorts](https://macports.org).
|
||||
* Windows: `choco install miller` using [Chocolatey](https://chocolatey.org).
|
||||
* You can get latest builds for Linux, MacOS, and Windows by visiting [https://github.com/johnkerl/miller/actions](https://github.com/johnkerl/miller/actions).
|
||||
, selecting the latest build, and clicking _Artifacts_. (These are retained for 5 days after each commit.)
|
||||
* See also the [build page](build.md) if you prefer -- in particular, if your platform's package manager doesn't have the latest release.
|
||||
|
||||
As a first check, you should be able to run ``mlr --version`` at your system's command prompt and see something like the following:
|
||||
As a first check, you should be able to run `mlr --version` at your system's command prompt and see something like the following:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --version</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
Miller v6.0.0-dev
|
||||
</pre>
|
||||
|
||||
As a second check, given [example.csv](./example.csv) you should be able to do
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --csv cat example.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color,shape,flag,index,quantity,rate
|
||||
yellow,triangle,true,11,43.6498,9.8870
|
||||
red,square,true,15,79.2778,0.0130
|
||||
|
|
@ -35,8 +40,10 @@ yellow,circle,true,87,63.5058,8.3350
|
|||
purple,square,false,91,72.3735,8.2430
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsv --opprint cat example.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color shape flag index quantity rate
|
||||
yellow triangle true 11 43.6498 9.8870
|
||||
red square true 15 79.2778 0.0130
|
||||
|
|
@ -56,10 +63,12 @@ If you run into issues on these checks, please check out the resources on the [c
|
|||
|
||||
Let's take a quick look at some of the most useful Miller verbs -- file-format-aware, name-index-empowered equivalents of standard system commands.
|
||||
|
||||
``mlr cat`` is like system ``cat`` (or ``type`` on Windows) -- it passes the data through unmodified:
|
||||
`mlr cat` is like system `cat` (or `type` on Windows) -- it passes the data through unmodified:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --csv cat example.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color,shape,flag,index,quantity,rate
|
||||
yellow,triangle,true,11,43.6498,9.8870
|
||||
red,square,true,15,79.2778,0.0130
|
||||
|
|
@ -73,10 +82,12 @@ yellow,circle,true,87,63.5058,8.3350
|
|||
purple,square,false,91,72.3735,8.2430
|
||||
</pre>
|
||||
|
||||
But ``mlr cat`` can also do format conversion -- for example, you can pretty-print in tabular format:
|
||||
But `mlr cat` can also do format conversion -- for example, you can pretty-print in tabular format:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsv --opprint cat example.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color shape flag index quantity rate
|
||||
yellow triangle true 11 43.6498 9.8870
|
||||
red square true 15 79.2778 0.0130
|
||||
|
|
@ -90,10 +101,12 @@ yellow circle true 87 63.5058 8.3350
|
|||
purple square false 91 72.3735 8.2430
|
||||
</pre>
|
||||
|
||||
``mlr head`` and ``mlr tail`` count records rather than lines. Whether you're getting the first few records or the last few, the CSV header is included either way:
|
||||
`mlr head` and `mlr tail` count records rather than lines. Whether you're getting the first few records or the last few, the CSV header is included either way:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --csv head -n 4 example.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color,shape,flag,index,quantity,rate
|
||||
yellow,triangle,true,11,43.6498,9.8870
|
||||
red,square,true,15,79.2778,0.0130
|
||||
|
|
@ -101,8 +114,10 @@ red,circle,true,16,13.8103,2.9010
|
|||
red,square,false,48,77.5542,7.4670
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --csv tail -n 4 example.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color,shape,flag,index,quantity,rate
|
||||
purple,triangle,false,65,80.1405,5.8240
|
||||
yellow,circle,true,73,63.9785,4.2370
|
||||
|
|
@ -110,8 +125,10 @@ yellow,circle,true,87,63.5058,8.3350
|
|||
purple,square,false,91,72.3735,8.2430
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsv --ojson tail -n 2 example.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
{
|
||||
"color": "yellow",
|
||||
"shape": "circle",
|
||||
|
|
@ -132,8 +149,10 @@ purple,square,false,91,72.3735,8.2430
|
|||
|
||||
You can sort on a single field:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsv --opprint sort -f shape example.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color shape flag index quantity rate
|
||||
red circle true 16 13.8103 2.9010
|
||||
yellow circle true 73 63.9785 4.2370
|
||||
|
|
@ -149,8 +168,10 @@ purple triangle false 65 80.1405 5.8240
|
|||
|
||||
Or, you can sort primarily alphabetically on one field, then secondarily numerically descending on another field, and so on:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsv --opprint sort -f shape -nr index example.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color shape flag index quantity rate
|
||||
yellow circle true 87 63.5058 8.3350
|
||||
yellow circle true 73 63.9785 4.2370
|
||||
|
|
@ -164,10 +185,12 @@ purple triangle false 51 81.2290 8.5910
|
|||
yellow triangle true 11 43.6498 9.8870
|
||||
</pre>
|
||||
|
||||
If there are fields you don't want to see in your data, you can use ``cut`` to keep only the ones you want, in the same order they appeared in the input data:
|
||||
If there are fields you don't want to see in your data, you can use `cut` to keep only the ones you want, in the same order they appeared in the input data:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsv --opprint cut -f flag,shape example.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
shape flag
|
||||
triangle true
|
||||
square true
|
||||
|
|
@ -181,10 +204,12 @@ circle true
|
|||
square false
|
||||
</pre>
|
||||
|
||||
You can also use ``cut -o`` to keep specified fields, but in your preferred order:
|
||||
You can also use `cut -o` to keep specified fields, but in your preferred order:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsv --opprint cut -o -f flag,shape example.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
flag shape
|
||||
true triangle
|
||||
true square
|
||||
|
|
@ -198,10 +223,12 @@ true circle
|
|||
false square
|
||||
</pre>
|
||||
|
||||
You can use ``cut -x`` to omit fields you don't care about:
|
||||
You can use `cut -x` to omit fields you don't care about:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsv --opprint cut -x -f flag,shape example.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color index quantity rate
|
||||
yellow 11 43.6498 9.8870
|
||||
red 15 79.2778 0.0130
|
||||
|
|
@ -215,10 +242,12 @@ yellow 87 63.5058 8.3350
|
|||
purple 91 72.3735 8.2430
|
||||
</pre>
|
||||
|
||||
You can use ``filter`` to keep only records you care about:
|
||||
You can use `filter` to keep only records you care about:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsv --opprint filter '$color == "red"' example.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color shape flag index quantity rate
|
||||
red square true 15 79.2778 0.0130
|
||||
red circle true 16 13.8103 2.9010
|
||||
|
|
@ -226,20 +255,24 @@ red square false 48 77.5542 7.4670
|
|||
red square false 64 77.1991 9.5310
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsv --opprint filter '$color == "red" && $flag == true' example.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color shape flag index quantity rate
|
||||
red square true 15 79.2778 0.0130
|
||||
red circle true 16 13.8103 2.9010
|
||||
</pre>
|
||||
|
||||
You can use ``put`` to create new fields which are computed from other fields:
|
||||
You can use `put` to create new fields which are computed from other fields:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsv --opprint put '</b>
|
||||
<b> $ratio = $quantity / $rate;</b>
|
||||
<b> $color_shape = $color . "_" . $shape</b>
|
||||
<b>' example.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color shape flag index quantity rate ratio color_shape
|
||||
yellow triangle true 11 43.6498 9.8870 4.414868008496004 yellow_triangle
|
||||
red square true 15 79.2778 0.0130 6098.292307692308 red_square
|
||||
|
|
@ -253,10 +286,12 @@ yellow circle true 87 63.5058 8.3350 7.619172165566886 yellow_circle
|
|||
purple square false 91 72.3735 8.2430 8.779995147397793 purple_square
|
||||
</pre>
|
||||
|
||||
Even though Miller's main selling point is name-indexing, sometimes you really want to refer to a field name by its positional index. Use ``$[[3]]`` to access the name of field 3 or ``$[[[3]]]`` to access the value of field 3:
|
||||
Even though Miller's main selling point is name-indexing, sometimes you really want to refer to a field name by its positional index. Use `$[[3]]` to access the name of field 3 or `$[[[3]]]` to access the value of field 3:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsv --opprint put '$[[3]] = "NEW"' example.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color shape NEW index quantity rate
|
||||
yellow triangle true 11 43.6498 9.8870
|
||||
red square true 15 79.2778 0.0130
|
||||
|
|
@ -270,8 +305,10 @@ yellow circle true 87 63.5058 8.3350
|
|||
purple square false 91 72.3735 8.2430
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsv --opprint put '$[[[3]]] = "NEW"' example.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color shape flag index quantity rate
|
||||
yellow triangle NEW 11 43.6498 9.8870
|
||||
red square NEW 15 79.2778 0.0130
|
||||
|
|
@ -289,23 +326,29 @@ You can find the full list of verbs at the [Verbs Reference](reference-verbs.md)
|
|||
|
||||
## Multiple input files
|
||||
|
||||
Miller takes all the files from the command line as an input stream. But it's format-aware, so it doesn't repeat CSV header lines. For example, with input files [data/a.csv](data/a.csv and [data/b.csv](data/b.csv), the system ``cat`` command will repeat header lines:
|
||||
Miller takes all the files from the command line as an input stream. But it's format-aware, so it doesn't repeat CSV header lines. For example, with input files [data/a.csv](data/a.csv and [data/b.csv](data/b.csv), the system `cat` command will repeat header lines:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/a.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a,b,c
|
||||
1,2,3
|
||||
4,5,6
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/b.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a,b,c
|
||||
7,8,9
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/a.csv data/b.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a,b,c
|
||||
1,2,3
|
||||
4,5,6
|
||||
|
|
@ -313,10 +356,12 @@ a,b,c
|
|||
7,8,9
|
||||
</pre>
|
||||
|
||||
However, ``mlr cat`` will not:
|
||||
However, `mlr cat` will not:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --csv cat data/a.csv data/b.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a,b,c
|
||||
1,2,3
|
||||
4,5,6
|
||||
|
|
@ -327,39 +372,47 @@ a,b,c
|
|||
|
||||
Often we want to chain queries together -- for example, sorting by a field and taking the top few values. We can do this using pipes:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --csv sort -nr index example.csv | mlr --icsv --opprint head -n 3</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color shape flag index quantity rate
|
||||
purple square false 91 72.3735 8.2430
|
||||
yellow circle true 87 63.5058 8.3350
|
||||
yellow circle true 73 63.9785 4.2370
|
||||
</pre>
|
||||
|
||||
This works fine -- but Miller also lets you chain verbs together using the word ``then``. Think of this as a Miller-internal pipe that lets you use fewer keystrokes:
|
||||
This works fine -- but Miller also lets you chain verbs together using the word `then`. Think of this as a Miller-internal pipe that lets you use fewer keystrokes:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsv --opprint sort -nr index then head -n 3 example.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color shape flag index quantity rate
|
||||
purple square false 91 72.3735 8.2430
|
||||
yellow circle true 87 63.5058 8.3350
|
||||
yellow circle true 73 63.9785 4.2370
|
||||
</pre>
|
||||
|
||||
As another convenience, you can put the filename first using ``--from``. When you're interacting with your data at the command line, this makes it easier to up-arrow and append to the previous command:
|
||||
As another convenience, you can put the filename first using `--from`. When you're interacting with your data at the command line, this makes it easier to up-arrow and append to the previous command:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsv --opprint --from example.csv sort -nr index then head -n 3</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color shape flag index quantity rate
|
||||
purple square false 91 72.3735 8.2430
|
||||
yellow circle true 87 63.5058 8.3350
|
||||
yellow circle true 73 63.9785 4.2370
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsv --opprint --from example.csv \</b>
|
||||
<b> sort -nr index \</b>
|
||||
<b> then head -n 3 \</b>
|
||||
<b> then cut -f shape,quantity</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
shape quantity
|
||||
square 72.3735
|
||||
circle 63.5058
|
||||
|
|
@ -368,22 +421,26 @@ circle 63.9785
|
|||
|
||||
## Sorts and stats
|
||||
|
||||
Now suppose you want to sort the data on a given column, *and then* take the top few in that ordering. You can use Miller's ``then`` feature to pipe commands together.
|
||||
Now suppose you want to sort the data on a given column, *and then* take the top few in that ordering. You can use Miller's `then` feature to pipe commands together.
|
||||
|
||||
Here are the records with the top three ``index`` values:
|
||||
Here are the records with the top three `index` values:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsv --opprint sort -nr index then head -n 3 example.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color shape flag index quantity rate
|
||||
purple square false 91 72.3735 8.2430
|
||||
yellow circle true 87 63.5058 8.3350
|
||||
yellow circle true 73 63.9785 4.2370
|
||||
</pre>
|
||||
|
||||
Lots of Miller commands take a ``-g`` option for group-by: here, ``head -n 1 -g shape`` outputs the first record for each distinct value of the ``shape`` field. This means we're finding the record with highest ``index`` field for each distinct ``shape`` field:
|
||||
Lots of Miller commands take a `-g` option for group-by: here, `head -n 1 -g shape` outputs the first record for each distinct value of the `shape` field. This means we're finding the record with highest `index` field for each distinct `shape` field:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsv --opprint sort -f shape -nr index then head -n 1 -g shape example.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color shape flag index quantity rate
|
||||
yellow circle true 87 63.5058 8.3350
|
||||
purple square false 91 72.3735 8.2430
|
||||
|
|
@ -392,18 +449,22 @@ purple triangle false 65 80.1405 5.8240
|
|||
|
||||
Statistics can be computed with or without group-by field(s):
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsv --opprint --from example.csv \</b>
|
||||
<b> stats1 -a count,min,mean,max -f quantity -g shape</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
shape quantity_count quantity_min quantity_mean quantity_max
|
||||
triangle 3 43.6498 68.33976666666666 81.229
|
||||
square 4 72.3735 76.60114999999999 79.2778
|
||||
circle 3 13.8103 47.0982 63.9785
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsv --opprint --from example.csv \</b>
|
||||
<b> stats1 -a count,min,mean,max -f quantity -g shape,color</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
shape color quantity_count quantity_min quantity_mean quantity_max
|
||||
triangle yellow 1 43.6498 43.6498 43.6498
|
||||
square red 3 77.1991 78.01036666666666 79.2778
|
||||
|
|
@ -415,9 +476,11 @@ square purple 1 72.3735 72.3735 72.3735
|
|||
|
||||
If your output has a lot of columns, you can use XTAB format to line things up vertically for you instead:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsv --oxtab --from example.csv \</b>
|
||||
<b> stats1 -a p0,p10,p25,p50,p75,p90,p99,p100 -f rate</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
rate_p0 0.0130
|
||||
rate_p10 2.9010
|
||||
rate_p25 4.2370
|
||||
|
|
@ -438,14 +501,14 @@ Miller supports the following formats:
|
|||
* JSON (JavaScript Object Notation)
|
||||
* PPRINT (pretty-printed tabular)
|
||||
* XTAB (vertical-tabular or sideways-tabular)
|
||||
* NIDX (numerically indexed, label-free, with implicit labels ``"1"``, ``"2"``, etc.)
|
||||
* NIDX (numerically indexed, label-free, with implicit labels `"1"`, `"2"`, etc.)
|
||||
* DKVP (delimited key-value pairs).
|
||||
|
||||
What's a CSV file, really? It's an array of rows, or *records*, each being a list of key-value pairs, or *fields*: for CSV it so happens that all the keys are shared in the header line and the values vary from one data line to another.
|
||||
|
||||
For example, if you have:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
shape,flag,index
|
||||
circle,1,24
|
||||
square,0,36
|
||||
|
|
@ -453,14 +516,14 @@ square,0,36
|
|||
|
||||
then that's a way of saying:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
shape=circle,flag=1,index=24
|
||||
shape=square,flag=0,index=36
|
||||
</pre>
|
||||
|
||||
Other ways to write the same data:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
CSV PPRINT
|
||||
shape,flag,index shape flag index
|
||||
circle,1,24 circle 1 24
|
||||
|
|
@ -487,111 +550,106 @@ Anything we can do with CSV input data, we can do with any other format input da
|
|||
|
||||
How to specify these to Miller:
|
||||
|
||||
* If you use ``--csv`` or ``--json`` or ``--pprint``, etc., then Miller will use that format for input and output.
|
||||
* If you use ``--icsv`` and ``--ojson`` (note the extra ``i`` and ``o``) then Miller will use CSV for input and JSON for output, etc. See also [Keystroke Savers](keystroke-savers.md) for even shorter options like ``--c2j``.
|
||||
* If you use `--csv` or `--json` or `--pprint`, etc., then Miller will use that format for input and output.
|
||||
* If you use `--icsv` and `--ojson` (note the extra `i` and `o`) then Miller will use CSV for input and JSON for output, etc. See also [Keystroke Savers](keystroke-savers.md) for even shorter options like `--c2j`.
|
||||
|
||||
You can read more about this at the [File Formats](file-formats.md) page.
|
||||
|
||||
.. _10min-choices-for-printing-to-files:
|
||||
|
||||
## Choices for printing to files
|
||||
|
||||
Often we want to print output to the screen. Miller does this by default, as we've seen in the previous examples.
|
||||
|
||||
Sometimes, though, we want to print output to another file. Just use **> outputfilenamegoeshere** at the end of your command:
|
||||
|
||||
.. code-block:: none
|
||||
:emphasize-lines: 1,1
|
||||
|
||||
mlr --icsv --opprint cat example.csv > newfile.csv
|
||||
# Output goes to the new file;
|
||||
# nothing is printed to the screen.
|
||||
|
||||
.. code-block:: none
|
||||
:emphasize-lines: 1,1
|
||||
|
||||
cat newfile.csv
|
||||
color shape flag index quantity rate
|
||||
yellow triangle true 11 43.6498 9.8870
|
||||
red square true 15 79.2778 0.0130
|
||||
red circle true 16 13.8103 2.9010
|
||||
red square false 48 77.5542 7.4670
|
||||
purple triangle false 51 81.2290 8.5910
|
||||
red square false 64 77.1991 9.5310
|
||||
purple triangle false 65 80.1405 5.8240
|
||||
yellow circle true 73 63.9785 4.2370
|
||||
yellow circle true 87 63.5058 8.3350
|
||||
purple square false 91 72.3735 8.2430
|
||||
|
||||
Other times we just want our files to be **changed in-place**: just use **mlr -I**:
|
||||
|
||||
.. code-block:: none
|
||||
:emphasize-lines: 1,1
|
||||
|
||||
cp example.csv newfile.txt
|
||||
|
||||
.. code-block:: none
|
||||
:emphasize-lines: 1,1
|
||||
|
||||
cat newfile.txt
|
||||
color,shape,flag,index,quantity,rate
|
||||
yellow,triangle,true,11,43.6498,9.8870
|
||||
red,square,true,15,79.2778,0.0130
|
||||
red,circle,true,16,13.8103,2.9010
|
||||
red,square,false,48,77.5542,7.4670
|
||||
purple,triangle,false,51,81.2290,8.5910
|
||||
red,square,false,64,77.1991,9.5310
|
||||
purple,triangle,false,65,80.1405,5.8240
|
||||
yellow,circle,true,73,63.9785,4.2370
|
||||
yellow,circle,true,87,63.5058,8.3350
|
||||
purple,square,false,91,72.3735,8.2430
|
||||
|
||||
.. code-block:: none
|
||||
:emphasize-lines: 1,1
|
||||
|
||||
mlr -I --csv sort -f shape newfile.txt
|
||||
|
||||
.. code-block:: none
|
||||
:emphasize-lines: 1,1
|
||||
|
||||
cat newfile.txt
|
||||
color,shape,flag,index,quantity,rate
|
||||
red,circle,true,16,13.8103,2.9010
|
||||
yellow,circle,true,73,63.9785,4.2370
|
||||
yellow,circle,true,87,63.5058,8.3350
|
||||
red,square,true,15,79.2778,0.0130
|
||||
red,square,false,48,77.5542,7.4670
|
||||
red,square,false,64,77.1991,9.5310
|
||||
purple,square,false,91,72.3735,8.2430
|
||||
yellow,triangle,true,11,43.6498,9.8870
|
||||
purple,triangle,false,51,81.2290,8.5910
|
||||
purple,triangle,false,65,80.1405,5.8240
|
||||
|
||||
Also using ``mlr -I`` you can bulk-operate on lots of files: e.g.:
|
||||
|
||||
.. code-block:: none
|
||||
:emphasize-lines: 1,1
|
||||
|
||||
mlr -I --csv cut -x -f unwanted_column_name *.csv
|
||||
|
||||
If you like, you can first copy off your original data somewhere else, before doing in-place operations.
|
||||
|
||||
Lastly, using ``tee`` within ``put``, you can split your input data into separate files per one or more field names:
|
||||
|
||||
<pre>
|
||||
<b>mlr --csv --from example.csv put -q 'tee > $shape.".csv", $*'</b>
|
||||
<b>mlr --icsv --opprint cat example.csv > newfile.csv</b>
|
||||
# Output goes to the new file;
|
||||
# nothing is printed to the screen.
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<b>cat newfile.csv</b>
|
||||
color shape flag index quantity rate
|
||||
yellow triangle true 11 43.6498 9.8870
|
||||
red square true 15 79.2778 0.0130
|
||||
red circle true 16 13.8103 2.9010
|
||||
red square false 48 77.5542 7.4670
|
||||
purple triangle false 51 81.2290 8.5910
|
||||
red square false 64 77.1991 9.5310
|
||||
purple triangle false 65 80.1405 5.8240
|
||||
yellow circle true 73 63.9785 4.2370
|
||||
yellow circle true 87 63.5058 8.3350
|
||||
purple square false 91 72.3735 8.2430
|
||||
</pre>
|
||||
|
||||
Other times we just want our files to be **changed in-place**: just use **mlr -I**:
|
||||
|
||||
<pre>
|
||||
<b>cp example.csv newfile.txt</b>
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<b>cat newfile.txt</b>
|
||||
color,shape,flag,index,quantity,rate
|
||||
yellow,triangle,true,11,43.6498,9.8870
|
||||
red,square,true,15,79.2778,0.0130
|
||||
red,circle,true,16,13.8103,2.9010
|
||||
red,square,false,48,77.5542,7.4670
|
||||
purple,triangle,false,51,81.2290,8.5910
|
||||
red,square,false,64,77.1991,9.5310
|
||||
purple,triangle,false,65,80.1405,5.8240
|
||||
yellow,circle,true,73,63.9785,4.2370
|
||||
yellow,circle,true,87,63.5058,8.3350
|
||||
purple,square,false,91,72.3735,8.2430
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<b>mlr -I --csv sort -f shape newfile.txt</b>
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<b>cat newfile.txt</b>
|
||||
color,shape,flag,index,quantity,rate
|
||||
red,circle,true,16,13.8103,2.9010
|
||||
yellow,circle,true,73,63.9785,4.2370
|
||||
yellow,circle,true,87,63.5058,8.3350
|
||||
red,square,true,15,79.2778,0.0130
|
||||
red,square,false,48,77.5542,7.4670
|
||||
red,square,false,64,77.1991,9.5310
|
||||
purple,square,false,91,72.3735,8.2430
|
||||
yellow,triangle,true,11,43.6498,9.8870
|
||||
purple,triangle,false,51,81.2290,8.5910
|
||||
purple,triangle,false,65,80.1405,5.8240
|
||||
</pre>
|
||||
|
||||
Also using `mlr -I` you can bulk-operate on lots of files: e.g.:
|
||||
|
||||
<pre>
|
||||
<b>mlr -I --csv cut -x -f unwanted_column_name *.csv</b>
|
||||
</pre>
|
||||
|
||||
If you like, you can first copy off your original data somewhere else, before doing in-place operations.
|
||||
|
||||
Lastly, using `tee` within `put`, you can split your input data into separate files per one or more field names:
|
||||
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --csv --from example.csv put -q 'tee > $shape.".csv", $*'</b>
|
||||
</pre>
|
||||
|
||||
<pre class="pre-highlight">
|
||||
<b>cat circle.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color,shape,flag,index,quantity,rate
|
||||
red,circle,true,16,13.8103,2.9010
|
||||
yellow,circle,true,73,63.9785,4.2370
|
||||
yellow,circle,true,87,63.5058,8.3350
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat square.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color,shape,flag,index,quantity,rate
|
||||
red,square,true,15,79.2778,0.0130
|
||||
red,square,false,48,77.5542,7.4670
|
||||
|
|
@ -599,8 +657,10 @@ red,square,false,64,77.1991,9.5310
|
|||
purple,square,false,91,72.3735,8.2430
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat triangle.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color,shape,flag,index,quantity,rate
|
||||
yellow,triangle,true,11,43.6498,9.8870
|
||||
purple,triangle,false,51,81.2290,8.5910
|
||||
|
|
|
|||
|
|
@ -4,13 +4,14 @@
|
|||
|
||||
You can install Miller for various platforms as follows:
|
||||
|
||||
* Linux: ``yum install miller`` or ``apt-get install miller`` depending on your flavor of Linux
|
||||
* MacOS: ``brew install miller`` or ``port install miller`` depending on your preference of [Homebrew](https://brew.sh>`_ or `MacPorts <https://macports.org).
|
||||
* Windows: ``choco install miller`` using [Chocolatey](https://chocolatey.org).
|
||||
* You can get latest builds for Linux, MacOS, and Windows by visiting https://github.com/johnkerl/miller/actions, selecting the latest build, and clicking _Artifacts_. (These are retained for 5 days after each commit.)
|
||||
* Linux: `yum install miller` or `apt-get install miller` depending on your flavor of Linux
|
||||
* MacOS: `brew install miller` or `port install miller` depending on your preference of [Homebrew](https://brew.sh) or [MacPorts](https://macports.org).
|
||||
* Windows: `choco install miller` using [Chocolatey](https://chocolatey.org).
|
||||
* You can get latest builds for Linux, MacOS, and Windows by visiting [https://github.com/johnkerl/miller/actions](https://github.com/johnkerl/miller/actions).
|
||||
, selecting the latest build, and clicking _Artifacts_. (These are retained for 5 days after each commit.)
|
||||
* See also the [build page](build.md) if you prefer -- in particular, if your platform's package manager doesn't have the latest release.
|
||||
|
||||
As a first check, you should be able to run ``mlr --version`` at your system's command prompt and see something like the following:
|
||||
As a first check, you should be able to run `mlr --version` at your system's command prompt and see something like the following:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --version
|
||||
|
|
@ -32,19 +33,19 @@ If you run into issues on these checks, please check out the resources on the [c
|
|||
|
||||
Let's take a quick look at some of the most useful Miller verbs -- file-format-aware, name-index-empowered equivalents of standard system commands.
|
||||
|
||||
``mlr cat`` is like system ``cat`` (or ``type`` on Windows) -- it passes the data through unmodified:
|
||||
`mlr cat` is like system `cat` (or `type` on Windows) -- it passes the data through unmodified:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --csv cat example.csv
|
||||
GENMD_EOF
|
||||
|
||||
But ``mlr cat`` can also do format conversion -- for example, you can pretty-print in tabular format:
|
||||
But `mlr cat` can also do format conversion -- for example, you can pretty-print in tabular format:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --icsv --opprint cat example.csv
|
||||
GENMD_EOF
|
||||
|
||||
``mlr head`` and ``mlr tail`` count records rather than lines. Whether you're getting the first few records or the last few, the CSV header is included either way:
|
||||
`mlr head` and `mlr tail` count records rather than lines. Whether you're getting the first few records or the last few, the CSV header is included either way:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --csv head -n 4 example.csv
|
||||
|
|
@ -70,25 +71,25 @@ GENMD_RUN_COMMAND
|
|||
mlr --icsv --opprint sort -f shape -nr index example.csv
|
||||
GENMD_EOF
|
||||
|
||||
If there are fields you don't want to see in your data, you can use ``cut`` to keep only the ones you want, in the same order they appeared in the input data:
|
||||
If there are fields you don't want to see in your data, you can use `cut` to keep only the ones you want, in the same order they appeared in the input data:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --icsv --opprint cut -f flag,shape example.csv
|
||||
GENMD_EOF
|
||||
|
||||
You can also use ``cut -o`` to keep specified fields, but in your preferred order:
|
||||
You can also use `cut -o` to keep specified fields, but in your preferred order:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --icsv --opprint cut -o -f flag,shape example.csv
|
||||
GENMD_EOF
|
||||
|
||||
You can use ``cut -x`` to omit fields you don't care about:
|
||||
You can use `cut -x` to omit fields you don't care about:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --icsv --opprint cut -x -f flag,shape example.csv
|
||||
GENMD_EOF
|
||||
|
||||
You can use ``filter`` to keep only records you care about:
|
||||
You can use `filter` to keep only records you care about:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --icsv --opprint filter '$color == "red"' example.csv
|
||||
|
|
@ -98,7 +99,7 @@ GENMD_RUN_COMMAND
|
|||
mlr --icsv --opprint filter '$color == "red" && $flag == true' example.csv
|
||||
GENMD_EOF
|
||||
|
||||
You can use ``put`` to create new fields which are computed from other fields:
|
||||
You can use `put` to create new fields which are computed from other fields:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --icsv --opprint put '
|
||||
|
|
@ -107,7 +108,7 @@ mlr --icsv --opprint put '
|
|||
' example.csv
|
||||
GENMD_EOF
|
||||
|
||||
Even though Miller's main selling point is name-indexing, sometimes you really want to refer to a field name by its positional index. Use ``$[[3]]`` to access the name of field 3 or ``$[[[3]]]`` to access the value of field 3:
|
||||
Even though Miller's main selling point is name-indexing, sometimes you really want to refer to a field name by its positional index. Use `$[[3]]` to access the name of field 3 or `$[[[3]]]` to access the value of field 3:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --icsv --opprint put '$[[3]] = "NEW"' example.csv
|
||||
|
|
@ -121,7 +122,7 @@ You can find the full list of verbs at the [Verbs Reference](reference-verbs.md)
|
|||
|
||||
## Multiple input files
|
||||
|
||||
Miller takes all the files from the command line as an input stream. But it's format-aware, so it doesn't repeat CSV header lines. For example, with input files [data/a.csv](data/a.csv and [data/b.csv](data/b.csv), the system ``cat`` command will repeat header lines:
|
||||
Miller takes all the files from the command line as an input stream. But it's format-aware, so it doesn't repeat CSV header lines. For example, with input files [data/a.csv](data/a.csv and [data/b.csv](data/b.csv), the system `cat` command will repeat header lines:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
cat data/a.csv
|
||||
|
|
@ -135,7 +136,7 @@ GENMD_RUN_COMMAND
|
|||
cat data/a.csv data/b.csv
|
||||
GENMD_EOF
|
||||
|
||||
However, ``mlr cat`` will not:
|
||||
However, `mlr cat` will not:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --csv cat data/a.csv data/b.csv
|
||||
|
|
@ -149,13 +150,13 @@ GENMD_RUN_COMMAND
|
|||
mlr --csv sort -nr index example.csv | mlr --icsv --opprint head -n 3
|
||||
GENMD_EOF
|
||||
|
||||
This works fine -- but Miller also lets you chain verbs together using the word ``then``. Think of this as a Miller-internal pipe that lets you use fewer keystrokes:
|
||||
This works fine -- but Miller also lets you chain verbs together using the word `then`. Think of this as a Miller-internal pipe that lets you use fewer keystrokes:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --icsv --opprint sort -nr index then head -n 3 example.csv
|
||||
GENMD_EOF
|
||||
|
||||
As another convenience, you can put the filename first using ``--from``. When you're interacting with your data at the command line, this makes it easier to up-arrow and append to the previous command:
|
||||
As another convenience, you can put the filename first using `--from`. When you're interacting with your data at the command line, this makes it easier to up-arrow and append to the previous command:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --icsv --opprint --from example.csv sort -nr index then head -n 3
|
||||
|
|
@ -170,15 +171,15 @@ GENMD_EOF
|
|||
|
||||
## Sorts and stats
|
||||
|
||||
Now suppose you want to sort the data on a given column, *and then* take the top few in that ordering. You can use Miller's ``then`` feature to pipe commands together.
|
||||
Now suppose you want to sort the data on a given column, *and then* take the top few in that ordering. You can use Miller's `then` feature to pipe commands together.
|
||||
|
||||
Here are the records with the top three ``index`` values:
|
||||
Here are the records with the top three `index` values:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --icsv --opprint sort -nr index then head -n 3 example.csv
|
||||
GENMD_EOF
|
||||
|
||||
Lots of Miller commands take a ``-g`` option for group-by: here, ``head -n 1 -g shape`` outputs the first record for each distinct value of the ``shape`` field. This means we're finding the record with highest ``index`` field for each distinct ``shape`` field:
|
||||
Lots of Miller commands take a `-g` option for group-by: here, `head -n 1 -g shape` outputs the first record for each distinct value of the `shape` field. This means we're finding the record with highest `index` field for each distinct `shape` field:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --icsv --opprint sort -f shape -nr index then head -n 1 -g shape example.csv
|
||||
|
|
@ -213,7 +214,7 @@ Miller supports the following formats:
|
|||
* JSON (JavaScript Object Notation)
|
||||
* PPRINT (pretty-printed tabular)
|
||||
* XTAB (vertical-tabular or sideways-tabular)
|
||||
* NIDX (numerically indexed, label-free, with implicit labels ``"1"``, ``"2"``, etc.)
|
||||
* NIDX (numerically indexed, label-free, with implicit labels `"1"`, `"2"`, etc.)
|
||||
* DKVP (delimited key-value pairs).
|
||||
|
||||
What's a CSV file, really? It's an array of rows, or *records*, each being a list of key-value pairs, or *fields*: for CSV it so happens that all the keys are shared in the header line and the values vary from one data line to another.
|
||||
|
|
@ -262,96 +263,87 @@ Anything we can do with CSV input data, we can do with any other format input da
|
|||
|
||||
How to specify these to Miller:
|
||||
|
||||
* If you use ``--csv`` or ``--json`` or ``--pprint``, etc., then Miller will use that format for input and output.
|
||||
* If you use ``--icsv`` and ``--ojson`` (note the extra ``i`` and ``o``) then Miller will use CSV for input and JSON for output, etc. See also [Keystroke Savers](keystroke-savers.md) for even shorter options like ``--c2j``.
|
||||
* If you use `--csv` or `--json` or `--pprint`, etc., then Miller will use that format for input and output.
|
||||
* If you use `--icsv` and `--ojson` (note the extra `i` and `o`) then Miller will use CSV for input and JSON for output, etc. See also [Keystroke Savers](keystroke-savers.md) for even shorter options like `--c2j`.
|
||||
|
||||
You can read more about this at the [File Formats](file-formats.md) page.
|
||||
|
||||
.. _10min-choices-for-printing-to-files:
|
||||
|
||||
## Choices for printing to files
|
||||
|
||||
Often we want to print output to the screen. Miller does this by default, as we've seen in the previous examples.
|
||||
|
||||
Sometimes, though, we want to print output to another file. Just use **> outputfilenamegoeshere** at the end of your command:
|
||||
|
||||
.. code-block:: none
|
||||
:emphasize-lines: 1,1
|
||||
<pre>
|
||||
<b>mlr --icsv --opprint cat example.csv > newfile.csv</b>
|
||||
# Output goes to the new file;
|
||||
# nothing is printed to the screen.
|
||||
</pre>
|
||||
|
||||
mlr --icsv --opprint cat example.csv > newfile.csv
|
||||
# Output goes to the new file;
|
||||
# nothing is printed to the screen.
|
||||
|
||||
.. code-block:: none
|
||||
:emphasize-lines: 1,1
|
||||
|
||||
cat newfile.csv
|
||||
color shape flag index quantity rate
|
||||
yellow triangle true 11 43.6498 9.8870
|
||||
red square true 15 79.2778 0.0130
|
||||
red circle true 16 13.8103 2.9010
|
||||
red square false 48 77.5542 7.4670
|
||||
purple triangle false 51 81.2290 8.5910
|
||||
red square false 64 77.1991 9.5310
|
||||
purple triangle false 65 80.1405 5.8240
|
||||
yellow circle true 73 63.9785 4.2370
|
||||
yellow circle true 87 63.5058 8.3350
|
||||
purple square false 91 72.3735 8.2430
|
||||
<pre>
|
||||
<b>cat newfile.csv</b>
|
||||
color shape flag index quantity rate
|
||||
yellow triangle true 11 43.6498 9.8870
|
||||
red square true 15 79.2778 0.0130
|
||||
red circle true 16 13.8103 2.9010
|
||||
red square false 48 77.5542 7.4670
|
||||
purple triangle false 51 81.2290 8.5910
|
||||
red square false 64 77.1991 9.5310
|
||||
purple triangle false 65 80.1405 5.8240
|
||||
yellow circle true 73 63.9785 4.2370
|
||||
yellow circle true 87 63.5058 8.3350
|
||||
purple square false 91 72.3735 8.2430
|
||||
</pre>
|
||||
|
||||
Other times we just want our files to be **changed in-place**: just use **mlr -I**:
|
||||
|
||||
.. code-block:: none
|
||||
:emphasize-lines: 1,1
|
||||
<pre>
|
||||
<b>cp example.csv newfile.txt</b>
|
||||
</pre>
|
||||
|
||||
cp example.csv newfile.txt
|
||||
<pre>
|
||||
<b>cat newfile.txt</b>
|
||||
color,shape,flag,index,quantity,rate
|
||||
yellow,triangle,true,11,43.6498,9.8870
|
||||
red,square,true,15,79.2778,0.0130
|
||||
red,circle,true,16,13.8103,2.9010
|
||||
red,square,false,48,77.5542,7.4670
|
||||
purple,triangle,false,51,81.2290,8.5910
|
||||
red,square,false,64,77.1991,9.5310
|
||||
purple,triangle,false,65,80.1405,5.8240
|
||||
yellow,circle,true,73,63.9785,4.2370
|
||||
yellow,circle,true,87,63.5058,8.3350
|
||||
purple,square,false,91,72.3735,8.2430
|
||||
</pre>
|
||||
|
||||
.. code-block:: none
|
||||
:emphasize-lines: 1,1
|
||||
<pre>
|
||||
<b>mlr -I --csv sort -f shape newfile.txt</b>
|
||||
</pre>
|
||||
|
||||
cat newfile.txt
|
||||
color,shape,flag,index,quantity,rate
|
||||
yellow,triangle,true,11,43.6498,9.8870
|
||||
red,square,true,15,79.2778,0.0130
|
||||
red,circle,true,16,13.8103,2.9010
|
||||
red,square,false,48,77.5542,7.4670
|
||||
purple,triangle,false,51,81.2290,8.5910
|
||||
red,square,false,64,77.1991,9.5310
|
||||
purple,triangle,false,65,80.1405,5.8240
|
||||
yellow,circle,true,73,63.9785,4.2370
|
||||
yellow,circle,true,87,63.5058,8.3350
|
||||
purple,square,false,91,72.3735,8.2430
|
||||
<pre>
|
||||
<b>cat newfile.txt</b>
|
||||
color,shape,flag,index,quantity,rate
|
||||
red,circle,true,16,13.8103,2.9010
|
||||
yellow,circle,true,73,63.9785,4.2370
|
||||
yellow,circle,true,87,63.5058,8.3350
|
||||
red,square,true,15,79.2778,0.0130
|
||||
red,square,false,48,77.5542,7.4670
|
||||
red,square,false,64,77.1991,9.5310
|
||||
purple,square,false,91,72.3735,8.2430
|
||||
yellow,triangle,true,11,43.6498,9.8870
|
||||
purple,triangle,false,51,81.2290,8.5910
|
||||
purple,triangle,false,65,80.1405,5.8240
|
||||
</pre>
|
||||
|
||||
.. code-block:: none
|
||||
:emphasize-lines: 1,1
|
||||
Also using `mlr -I` you can bulk-operate on lots of files: e.g.:
|
||||
|
||||
mlr -I --csv sort -f shape newfile.txt
|
||||
|
||||
.. code-block:: none
|
||||
:emphasize-lines: 1,1
|
||||
|
||||
cat newfile.txt
|
||||
color,shape,flag,index,quantity,rate
|
||||
red,circle,true,16,13.8103,2.9010
|
||||
yellow,circle,true,73,63.9785,4.2370
|
||||
yellow,circle,true,87,63.5058,8.3350
|
||||
red,square,true,15,79.2778,0.0130
|
||||
red,square,false,48,77.5542,7.4670
|
||||
red,square,false,64,77.1991,9.5310
|
||||
purple,square,false,91,72.3735,8.2430
|
||||
yellow,triangle,true,11,43.6498,9.8870
|
||||
purple,triangle,false,51,81.2290,8.5910
|
||||
purple,triangle,false,65,80.1405,5.8240
|
||||
|
||||
Also using ``mlr -I`` you can bulk-operate on lots of files: e.g.:
|
||||
|
||||
.. code-block:: none
|
||||
:emphasize-lines: 1,1
|
||||
|
||||
mlr -I --csv cut -x -f unwanted_column_name *.csv
|
||||
<pre>
|
||||
<b>mlr -I --csv cut -x -f unwanted_column_name *.csv</b>
|
||||
</pre>
|
||||
|
||||
If you like, you can first copy off your original data somewhere else, before doing in-place operations.
|
||||
|
||||
Lastly, using ``tee`` within ``put``, you can split your input data into separate files per one or more field names:
|
||||
Lastly, using `tee` within `put`, you can split your input data into separate files per one or more field names:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --csv --from example.csv put -q 'tee > $shape.".csv", $*'
|
||||
|
|
|
|||
|
|
@ -1,40 +0,0 @@
|
|||
# Miller Sphinx docs
|
||||
|
||||
## Why use Sphinx
|
||||
|
||||
* Connects to https://miller.readthedocs.io so people can get their docmods onto the web instead of the self-hosted https://johnkerl.org/miller/doc. Thanks to @pabloab for the great advice!
|
||||
* More standard look and feel -- lots of people use readthedocs for other things so this should feel familiar
|
||||
* We get a Search feature for free
|
||||
|
||||
## Contributing
|
||||
|
||||
* You need `pip install sphinx` (or `pip3 install sphinx`)
|
||||
* The docs include lots of live code examples which will be invoked using `mlr` which must be somewhere in your `$PATH`
|
||||
* Clone https://github.com/johnkerl/miller and cd into `docs/` within your clone
|
||||
* Editing loop:
|
||||
* Edit `*.md.in`
|
||||
* Run `make html`
|
||||
* Either `open _build/html/index.html` (MacOS) or point your browser to `file:///path/to/your/clone/of/miller/docs/_build/html/index.html`
|
||||
* Submitting:
|
||||
* `git add` your modified files, `git commit`, `git push`, and submit a PR at https://github.com/johnkerl/miller
|
||||
* A nice markup reference: https://www.sphinx-doc.org/en/1.8/usage/restructuredtext/basics.html
|
||||
|
||||
## Notes
|
||||
|
||||
* CSS:
|
||||
* I used the Sphinx Classic theme which I like a lot except the colors -- it's a blue scheme and Miller has never been blue.
|
||||
* Files are in `docs/_static/*.css` where I marked my mods with `/* CHANGE ME */`.
|
||||
* If you modify the CSS you must run `make clean html` (not just `make html`) then reload in your browser.
|
||||
* Live code:
|
||||
* I didn't find a way to include non-Python live-code examples within Sphinx so I adapted the pre-Sphinx Miller-doc strategy which is to have a generator script read a template file (here, `foo.md.in`), run the marked lines, and generate the output file (`foo.md`).
|
||||
* Edit the `*.md.in` files, not `*.md` directly.
|
||||
* Within the `*.md.in` files are lines like `GENMD_RUN_COMMAND`. These will be run, and their output included, by `make html` which calls the `genmds` script for you.
|
||||
* readthedocs:
|
||||
* https://readthedocs.org/
|
||||
* https://readthedocs.org/projects/miller/
|
||||
* https://readthedocs.org/projects/miller/builds/
|
||||
* https://miller.readthedocs.io/en/latest/
|
||||
|
||||
## To do
|
||||
|
||||
* Let's all discuss if/how we want the v2 docs to be structured better than the v1 docs.
|
||||
|
|
@ -5,40 +5,40 @@ Please also see [Installation](installation.md) for information about pre-built
|
|||
|
||||
## Miller license
|
||||
|
||||
Two-clause BSD license https://github.com/johnkerl/miller/blob/master/LICENSE.txt.
|
||||
Two-clause BSD license [https://github.com/johnkerl/miller/blob/master/LICENSE.txt](https://github.com/johnkerl/miller/blob/master/LICENSE.txt).
|
||||
|
||||
## From release tarball
|
||||
|
||||
* Obtain ``mlr-i.j.k.tar.gz`` from https://github.com/johnkerl/miller/tags, replacing ``i.j.k`` with the desired release, e.g. ``6.1.0``.
|
||||
* ``tar zxvf mlr-i.j.k.tar.gz``
|
||||
* ``cd mlr-i.j.k``
|
||||
* ``cd go``
|
||||
* ``./build`` creates the ``go/mlr`` executable and runs regression tests
|
||||
* ``go build mlr.go`` creates the ``go/mlr`` executable without running regression tests
|
||||
* Obtain `mlr-i.j.k.tar.gz` from [https://github.com/johnkerl/miller/tags](https://github.com/johnkerl/miller/tags), replacing `i.j.k` with the desired release, e.g. `6.1.0`.
|
||||
* `tar zxvf mlr-i.j.k.tar.gz`
|
||||
* `cd mlr-i.j.k`
|
||||
* `cd go`
|
||||
* `./build` creates the `go/mlr` executable and runs regression tests
|
||||
* `go build mlr.go` creates the `go/mlr` executable without running regression tests
|
||||
|
||||
## From git clone
|
||||
|
||||
* ``git clone https://github.com/johnkerl/miller``
|
||||
* ``cd miller/go``
|
||||
* ``./build`` creates the ``go/mlr`` executable and runs regression tests
|
||||
* ``go build mlr.go`` creates the ``go/mlr`` executable without running regression tests
|
||||
* `git clone https://github.com/johnkerl/miller`
|
||||
* `cd miller/go`
|
||||
* `./build` creates the `go/mlr` executable and runs regression tests
|
||||
* `go build mlr.go` creates the `go/mlr` executable without running regression tests
|
||||
|
||||
## In case of problems
|
||||
|
||||
If you have any build errors, feel free to open an issue with "New Issue" at https://github.com/johnkerl/miller/issues.
|
||||
If you have any build errors, feel free to open an issue with "New Issue" at [https://github.com/johnkerl/miller/issues](https://github.com/johnkerl/miller/issues).
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Required external dependencies
|
||||
|
||||
These are necessary to produce the ``mlr`` executable.
|
||||
These are necessary to produce the `mlr` executable.
|
||||
|
||||
* Go version 1.16 or higher
|
||||
* Others packaged within ``go.mod`` and ``go.sum`` which you don't need to deal with manually -- the Go build process handles them for us
|
||||
* Others packaged within `go.mod` and `go.sum` which you don't need to deal with manually -- the Go build process handles them for us
|
||||
|
||||
### Optional external dependencies
|
||||
|
||||
This documentation pageset is built using Sphinx. Please see https://github.com/johnkerl/miller/blob/main/docs6/README.md for details.
|
||||
This documentation pageset is built using Sphinx. Please see [https://github.com/johnkerl/miller/blob/main/docs6/README.md](https://github.com/johnkerl/miller/blob/main/docs6/README.md) for details.
|
||||
|
||||
## Creating a new release: for developers
|
||||
|
||||
|
|
@ -46,40 +46,40 @@ At present I'm the primary developer so this is just my checklist for making new
|
|||
|
||||
In this example I am using version 6.1.0 to 6.2.0; of course that will change for subsequent revisions.
|
||||
|
||||
* Update version found in ``mlr --version`` and ``man mlr``:
|
||||
* Update version found in `mlr --version` and `man mlr`:
|
||||
|
||||
* Edit ``go/src/version/version.go`` from ``6.1.0-dev`` to ``6.2.0``.
|
||||
* Likewise ``docs6/conf.py``
|
||||
* ``cd ../docs6``
|
||||
* ``export PATH=../go:$PATH``
|
||||
* ``make html``
|
||||
* The ordering is important: the first build creates ``mlr``; the second runs ``mlr`` to create ``manpage.txt``; the third includes ``manpage.txt`` into one of its outputs.
|
||||
* Edit `go/src/version/version.go` from `6.1.0-dev` to `6.2.0`.
|
||||
* Likewise `docs6/conf.py`
|
||||
* `cd ../docs6`
|
||||
* `export PATH=../go:$PATH`
|
||||
* `make html`
|
||||
* The ordering is important: the first build creates `mlr`; the second runs `mlr` to create `manpage.txt`; the third includes `manpage.txt` into one of its outputs.
|
||||
* Commit and push.
|
||||
|
||||
* Create the release tarball and SRPM:
|
||||
|
||||
* TBD for the Go port ...
|
||||
* Linux/MacOS/Windows binaries from GitHub Actions ...
|
||||
* Pull back release tarball ``mlr-6.2.0.tar.gz`` from buildbox, and ``mlr.{arch}`` binaries from whatever buildboxes.
|
||||
* Pull back release tarball `mlr-6.2.0.tar.gz` from buildbox, and `mlr.{arch}` binaries from whatever buildboxes.
|
||||
|
||||
* Create the Github release tag:
|
||||
|
||||
* Don't forget the ``v`` in ``v6.2.0``
|
||||
* Don't forget the `v` in `v6.2.0`
|
||||
* Write the release notes
|
||||
* Attach the release tarball and binaries. Double-check assets were successfully uploaded.
|
||||
* Publish the release
|
||||
|
||||
* Check the release-specific docs:
|
||||
|
||||
* Look at https://miller.readthedocs.io for new-version docs, after a few minutes' propagation time.
|
||||
* Look at [https://miller.readthedocs.io](https://miller.readthedocs.io) for new-version docs, after a few minutes' propagation time.
|
||||
|
||||
* Notify:
|
||||
|
||||
* Submit ``brew`` pull request; notify any other distros which don't appear to have autoupdated since the previous release (notes below)
|
||||
* Similarly for ``macports``: https://github.com/macports/macports-ports/blob/master/textproc/miller/Portfile.
|
||||
* Submit `brew` pull request; notify any other distros which don't appear to have autoupdated since the previous release (notes below)
|
||||
* Similarly for `macports`: [https://github.com/macports/macports-ports/blob/master/textproc/miller/Portfile](https://github.com/macports/macports-ports/blob/master/textproc/miller/Portfile)
|
||||
* Social-media updates.
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
git remote add upstream https://github.com/Homebrew/homebrew-core # one-time setup only
|
||||
git fetch upstream
|
||||
git rebase upstream/master
|
||||
|
|
@ -88,8 +88,9 @@ shasum -a 256 /path/to/mlr-6.1.0.tar.gz
|
|||
edit Formula/miller.rb
|
||||
# Test the URL from the line like
|
||||
# url "https://github.com/johnkerl/miller/releases/download/v6.1.0/mlr-6.1.0.tar.gz"
|
||||
# in a browser for typos
|
||||
# A '@BrewTestBot Test this please' comment within the homebrew-core pull request will restart the homebrew travis build
|
||||
# in a browser for typos.
|
||||
# A '@BrewTestBot Test this please' comment within the homebrew-core pull request
|
||||
# will restart the homebrew travis build.
|
||||
git add Formula/miller.rb
|
||||
git commit -m 'miller 6.1.0'
|
||||
git push -u origin miller-6.1.0
|
||||
|
|
@ -98,9 +99,9 @@ git push -u origin miller-6.1.0
|
|||
|
||||
* Afterwork:
|
||||
|
||||
* Edit ``go/src/version/version.go`` and ``docs6/conf.py`` to change version from ``6.2.0`` to ``6.2.0-dev``.
|
||||
* ``cd go``
|
||||
* ``./build``
|
||||
* Edit `go/src/version/version.go` and `docs6/conf.py` to change version from `6.2.0` to `6.2.0-dev`.
|
||||
* `cd go`
|
||||
* `./build`
|
||||
* Commit and push.
|
||||
|
||||
## Misc. development notes
|
||||
|
|
|
|||
|
|
@ -4,40 +4,40 @@ Please also see [Installation](installation.md) for information about pre-built
|
|||
|
||||
## Miller license
|
||||
|
||||
Two-clause BSD license https://github.com/johnkerl/miller/blob/master/LICENSE.txt.
|
||||
Two-clause BSD license [https://github.com/johnkerl/miller/blob/master/LICENSE.txt](https://github.com/johnkerl/miller/blob/master/LICENSE.txt).
|
||||
|
||||
## From release tarball
|
||||
|
||||
* Obtain ``mlr-i.j.k.tar.gz`` from https://github.com/johnkerl/miller/tags, replacing ``i.j.k`` with the desired release, e.g. ``6.1.0``.
|
||||
* ``tar zxvf mlr-i.j.k.tar.gz``
|
||||
* ``cd mlr-i.j.k``
|
||||
* ``cd go``
|
||||
* ``./build`` creates the ``go/mlr`` executable and runs regression tests
|
||||
* ``go build mlr.go`` creates the ``go/mlr`` executable without running regression tests
|
||||
* Obtain `mlr-i.j.k.tar.gz` from [https://github.com/johnkerl/miller/tags](https://github.com/johnkerl/miller/tags), replacing `i.j.k` with the desired release, e.g. `6.1.0`.
|
||||
* `tar zxvf mlr-i.j.k.tar.gz`
|
||||
* `cd mlr-i.j.k`
|
||||
* `cd go`
|
||||
* `./build` creates the `go/mlr` executable and runs regression tests
|
||||
* `go build mlr.go` creates the `go/mlr` executable without running regression tests
|
||||
|
||||
## From git clone
|
||||
|
||||
* ``git clone https://github.com/johnkerl/miller``
|
||||
* ``cd miller/go``
|
||||
* ``./build`` creates the ``go/mlr`` executable and runs regression tests
|
||||
* ``go build mlr.go`` creates the ``go/mlr`` executable without running regression tests
|
||||
* `git clone https://github.com/johnkerl/miller`
|
||||
* `cd miller/go`
|
||||
* `./build` creates the `go/mlr` executable and runs regression tests
|
||||
* `go build mlr.go` creates the `go/mlr` executable without running regression tests
|
||||
|
||||
## In case of problems
|
||||
|
||||
If you have any build errors, feel free to open an issue with "New Issue" at https://github.com/johnkerl/miller/issues.
|
||||
If you have any build errors, feel free to open an issue with "New Issue" at [https://github.com/johnkerl/miller/issues](https://github.com/johnkerl/miller/issues).
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Required external dependencies
|
||||
|
||||
These are necessary to produce the ``mlr`` executable.
|
||||
These are necessary to produce the `mlr` executable.
|
||||
|
||||
* Go version 1.16 or higher
|
||||
* Others packaged within ``go.mod`` and ``go.sum`` which you don't need to deal with manually -- the Go build process handles them for us
|
||||
* Others packaged within `go.mod` and `go.sum` which you don't need to deal with manually -- the Go build process handles them for us
|
||||
|
||||
### Optional external dependencies
|
||||
|
||||
This documentation pageset is built using Sphinx. Please see https://github.com/johnkerl/miller/blob/main/docs6/README.md for details.
|
||||
This documentation pageset is built using Sphinx. Please see [https://github.com/johnkerl/miller/blob/main/docs6/README.md](https://github.com/johnkerl/miller/blob/main/docs6/README.md) for details.
|
||||
|
||||
## Creating a new release: for developers
|
||||
|
||||
|
|
@ -45,37 +45,37 @@ At present I'm the primary developer so this is just my checklist for making new
|
|||
|
||||
In this example I am using version 6.1.0 to 6.2.0; of course that will change for subsequent revisions.
|
||||
|
||||
* Update version found in ``mlr --version`` and ``man mlr``:
|
||||
* Update version found in `mlr --version` and `man mlr`:
|
||||
|
||||
* Edit ``go/src/version/version.go`` from ``6.1.0-dev`` to ``6.2.0``.
|
||||
* Likewise ``docs6/conf.py``
|
||||
* ``cd ../docs6``
|
||||
* ``export PATH=../go:$PATH``
|
||||
* ``make html``
|
||||
* The ordering is important: the first build creates ``mlr``; the second runs ``mlr`` to create ``manpage.txt``; the third includes ``manpage.txt`` into one of its outputs.
|
||||
* Edit `go/src/version/version.go` from `6.1.0-dev` to `6.2.0`.
|
||||
* Likewise `docs6/conf.py`
|
||||
* `cd ../docs6`
|
||||
* `export PATH=../go:$PATH`
|
||||
* `make html`
|
||||
* The ordering is important: the first build creates `mlr`; the second runs `mlr` to create `manpage.txt`; the third includes `manpage.txt` into one of its outputs.
|
||||
* Commit and push.
|
||||
|
||||
* Create the release tarball and SRPM:
|
||||
|
||||
* TBD for the Go port ...
|
||||
* Linux/MacOS/Windows binaries from GitHub Actions ...
|
||||
* Pull back release tarball ``mlr-6.2.0.tar.gz`` from buildbox, and ``mlr.{arch}`` binaries from whatever buildboxes.
|
||||
* Pull back release tarball `mlr-6.2.0.tar.gz` from buildbox, and `mlr.{arch}` binaries from whatever buildboxes.
|
||||
|
||||
* Create the Github release tag:
|
||||
|
||||
* Don't forget the ``v`` in ``v6.2.0``
|
||||
* Don't forget the `v` in `v6.2.0`
|
||||
* Write the release notes
|
||||
* Attach the release tarball and binaries. Double-check assets were successfully uploaded.
|
||||
* Publish the release
|
||||
|
||||
* Check the release-specific docs:
|
||||
|
||||
* Look at https://miller.readthedocs.io for new-version docs, after a few minutes' propagation time.
|
||||
* Look at [https://miller.readthedocs.io](https://miller.readthedocs.io) for new-version docs, after a few minutes' propagation time.
|
||||
|
||||
* Notify:
|
||||
|
||||
* Submit ``brew`` pull request; notify any other distros which don't appear to have autoupdated since the previous release (notes below)
|
||||
* Similarly for ``macports``: https://github.com/macports/macports-ports/blob/master/textproc/miller/Portfile.
|
||||
* Submit `brew` pull request; notify any other distros which don't appear to have autoupdated since the previous release (notes below)
|
||||
* Similarly for `macports`: [https://github.com/macports/macports-ports/blob/master/textproc/miller/Portfile](https://github.com/macports/macports-ports/blob/master/textproc/miller/Portfile)
|
||||
* Social-media updates.
|
||||
|
||||
GENMD_CARDIFY
|
||||
|
|
@ -87,8 +87,9 @@ shasum -a 256 /path/to/mlr-6.1.0.tar.gz
|
|||
edit Formula/miller.rb
|
||||
# Test the URL from the line like
|
||||
# url "https://github.com/johnkerl/miller/releases/download/v6.1.0/mlr-6.1.0.tar.gz"
|
||||
# in a browser for typos
|
||||
# A '@BrewTestBot Test this please' comment within the homebrew-core pull request will restart the homebrew travis build
|
||||
# in a browser for typos.
|
||||
# A '@BrewTestBot Test this please' comment within the homebrew-core pull request
|
||||
# will restart the homebrew travis build.
|
||||
git add Formula/miller.rb
|
||||
git commit -m 'miller 6.1.0'
|
||||
git push -u origin miller-6.1.0
|
||||
|
|
@ -97,9 +98,9 @@ GENMD_EOF
|
|||
|
||||
* Afterwork:
|
||||
|
||||
* Edit ``go/src/version/version.go`` and ``docs6/conf.py`` to change version from ``6.2.0`` to ``6.2.0-dev``.
|
||||
* ``cd go``
|
||||
* ``./build``
|
||||
* Edit `go/src/version/version.go` and `docs6/conf.py` to change version from `6.2.0` to `6.2.0-dev`.
|
||||
* `cd go`
|
||||
* `./build`
|
||||
* Commit and push.
|
||||
|
||||
## Misc. development notes
|
||||
|
|
|
|||
|
|
@ -2,6 +2,6 @@
|
|||
# Community
|
||||
|
||||
* See [Miller GitHub Discussions](https://github.com/johnkerl/miller/discussions) for general Q&A, advice, sharing success stories, etc.
|
||||
* See also [Miller-tagged questions on Stack Overflow](https://stackoverflow.com/questions/tagged/miller?tab=Newest)
|
||||
* See [Miller GitHub Issues](https://github.com/johnkerl/miller/issues) for bug reports and feature requests
|
||||
* Other correspondence: [mailto:kerl.john.r+miller@gmail.com](mailto:kerl.john.r+miller@gmail.com)
|
||||
* See also [Miller-tagged questions on Stack Overflow](https://stackoverflow.com/questions/tagged/miller?tab=Newest).
|
||||
* See [Miller GitHub Issues](https://github.com/johnkerl/miller/issues) for bug reports and feature requests.
|
||||
* Other correspondence: [mailto:kerl.john.r+miller@gmail.com](mailto:kerl.john.r+miller@gmail.com).
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Community
|
||||
|
||||
* See [Miller GitHub Discussions](https://github.com/johnkerl/miller/discussions) for general Q&A, advice, sharing success stories, etc.
|
||||
* See also [Miller-tagged questions on Stack Overflow](https://stackoverflow.com/questions/tagged/miller?tab=Newest)
|
||||
* See [Miller GitHub Issues](https://github.com/johnkerl/miller/issues) for bug reports and feature requests
|
||||
* Other correspondence: [mailto:kerl.john.r+miller@gmail.com](mailto:kerl.john.r+miller@gmail.com)
|
||||
* See also [Miller-tagged questions on Stack Overflow](https://stackoverflow.com/questions/tagged/miller?tab=Newest).
|
||||
* See [Miller GitHub Issues](https://github.com/johnkerl/miller/issues) for bug reports and feature requests.
|
||||
* Other correspondence: [mailto:kerl.john.r+miller@gmail.com](mailto:kerl.john.r+miller@gmail.com).
|
||||
|
|
|
|||
|
|
@ -11,15 +11,15 @@ Pre-release Miller documentation is at [https://github.com/johnkerl/miller/tree/
|
|||
|
||||
Clone [https://github.com/johnkerl/miller](https://github.com/johnkerl/miller) and `cd` into `docs6`.
|
||||
|
||||
After ``sudo pip install sphinx`` (or ``pip3``) you should be able to do ``make html``.
|
||||
After `sudo pip install sphinx` (or `pip3`) you should be able to do `make html`.
|
||||
|
||||
Edit ``*.md.in`` files, then ``make html`` to generate ``*.md``, then run the Sphinx document-generator.
|
||||
Edit `*.md.in` files, then `make html` to generate `*.md`, then run the Sphinx document-generator.
|
||||
|
||||
Open ``_build/html/index.html`` in your browser, e.g. ``file:////Users/yourname/git/miller/docs6/_build/html/contributing.html``, to verify.
|
||||
Open `_build/html/index.html` in your browser, e.g. `file:////Users/yourname/git/miller/docs6/_build/html/contributing.html`, to verify.
|
||||
|
||||
PRs are welcome at [https://github.com/johnkerl/miller](https://github.com/johnkerl/miller).
|
||||
|
||||
Once PRs are merged, readthedocs creates https://miller.readthedocs.io using the following configs:
|
||||
Once PRs are merged, readthedocs creates [https://miller.readthedocs.io](https://miller.readthedocs.io) using the following configs:
|
||||
|
||||
* [https://readthedocs.org/projects/miller](https://readthedocs.org/projects/miller)
|
||||
* [https://readthedocs.org/projects/miller/builds](https://readthedocs.org/projects/miller/builds)
|
||||
|
|
|
|||
|
|
@ -10,15 +10,15 @@ Pre-release Miller documentation is at [https://github.com/johnkerl/miller/tree/
|
|||
|
||||
Clone [https://github.com/johnkerl/miller](https://github.com/johnkerl/miller) and `cd` into `docs6`.
|
||||
|
||||
After ``sudo pip install sphinx`` (or ``pip3``) you should be able to do ``make html``.
|
||||
After `sudo pip install sphinx` (or `pip3`) you should be able to do `make html`.
|
||||
|
||||
Edit ``*.md.in`` files, then ``make html`` to generate ``*.md``, then run the Sphinx document-generator.
|
||||
Edit `*.md.in` files, then `make html` to generate `*.md`, then run the Sphinx document-generator.
|
||||
|
||||
Open ``_build/html/index.html`` in your browser, e.g. ``file:////Users/yourname/git/miller/docs6/_build/html/contributing.html``, to verify.
|
||||
Open `_build/html/index.html` in your browser, e.g. `file:////Users/yourname/git/miller/docs6/_build/html/contributing.html`, to verify.
|
||||
|
||||
PRs are welcome at [https://github.com/johnkerl/miller](https://github.com/johnkerl/miller).
|
||||
|
||||
Once PRs are merged, readthedocs creates https://miller.readthedocs.io using the following configs:
|
||||
Once PRs are merged, readthedocs creates [https://miller.readthedocs.io](https://miller.readthedocs.io) using the following configs:
|
||||
|
||||
* [https://readthedocs.org/projects/miller](https://readthedocs.org/projects/miller)
|
||||
* [https://readthedocs.org/projects/miller/builds](https://readthedocs.org/projects/miller/builds)
|
||||
|
|
|
|||
|
|
@ -3,20 +3,24 @@
|
|||
|
||||
## Headerless CSV on input or output
|
||||
|
||||
Sometimes we get CSV files which lack a header. For example (`data/headerless.csv <./data/headerless.csv>`_):
|
||||
Sometimes we get CSV files which lack a header. For example, [data/headerless.csv](./data/headerless.csv):
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/headerless.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
John,23,present
|
||||
Fred,34,present
|
||||
Alice,56,missing
|
||||
Carol,45,present
|
||||
</pre>
|
||||
|
||||
You can use Miller to add a header. The ``--implicit-csv-header`` applies positionally indexed labels:
|
||||
You can use Miller to add a header. The `--implicit-csv-header` applies positionally indexed labels:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --csv --implicit-csv-header cat data/headerless.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
1,2,3
|
||||
John,23,present
|
||||
Fred,34,present
|
||||
|
|
@ -26,8 +30,10 @@ Carol,45,present
|
|||
|
||||
Following that, you can rename the positionally indexed labels to names with meaning for your context. For example:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --csv --implicit-csv-header label name,age,status data/headerless.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
name,age,status
|
||||
John,23,present
|
||||
Fred,34,present
|
||||
|
|
@ -35,10 +41,12 @@ Alice,56,missing
|
|||
Carol,45,present
|
||||
</pre>
|
||||
|
||||
Likewise, if you need to produce CSV which is lacking its header, you can pipe Miller's output to the system command ``sed 1d``, or you can use Miller's ``--headerless-csv-output`` option:
|
||||
Likewise, if you need to produce CSV which is lacking its header, you can pipe Miller's output to the system command `sed 1d`, or you can use Miller's `--headerless-csv-output` option:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>head -5 data/colored-shapes.dkvp | mlr --ocsv cat</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color,shape,flag,i,u,v,w,x
|
||||
yellow,triangle,1,11,0.6321695890307647,0.9887207810889004,0.4364983936735774,5.7981881667050565
|
||||
red,square,1,15,0.21966833570651523,0.001257332190235938,0.7927778364718627,2.944117399716207
|
||||
|
|
@ -47,8 +55,10 @@ red,square,0,48,0.9562743938458542,0.7467203085342884,0.7755423050923582,7.11783
|
|||
purple,triangle,0,51,0.4355354501763202,0.8591292672156728,0.8122903963006748,5.753094629505863
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>head -5 data/colored-shapes.dkvp | mlr --ocsv --headerless-csv-output cat</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
yellow,triangle,1,11,0.6321695890307647,0.9887207810889004,0.4364983936735774,5.7981881667050565
|
||||
red,square,1,15,0.21966833570651523,0.001257332190235938,0.7927778364718627,2.944117399716207
|
||||
red,circle,1,16,0.20901671281497636,0.29005231936593445,0.13810280912907674,5.065034003400998
|
||||
|
|
@ -58,8 +68,10 @@ purple,triangle,0,51,0.4355354501763202,0.8591292672156728,0.8122903963006748,5.
|
|||
|
||||
Lastly, often we say "CSV" or "TSV" when we have positionally indexed data in columns which are separated by commas or tabs, respectively. In this case it's perhaps simpler to **just use NIDX format** which was designed for this purpose. (See also [File Formats](file-formats.md).) For example:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --inidx --ifs comma --oxtab cut -f 1,3 data/headerless.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
1 John
|
||||
3 present
|
||||
|
||||
|
|
@ -75,18 +87,20 @@ Lastly, often we say "CSV" or "TSV" when we have positionally indexed data in co
|
|||
|
||||
## Headerless CSV with duplicate field values
|
||||
|
||||
Miller is (by central design) a mapping from name to value, rather than integer position to value as in most tools in the Unix toolkit such as ``sort``, ``cut``, ``awk``, etc. So given input ``Yea=1,Yea=2`` on the same input line, first ``Yea=1`` is stored, then updated with ``Yea=2``. This is in the input-parser and the value ``Yea=1`` is unavailable to any further processing. The following example line comes from a headerless CSV file and includes 5 times the string (value) ``'NA'``:
|
||||
Miller is (by central design) a mapping from name to value, rather than integer position to value as in most tools in the Unix toolkit such as `sort`, `cut`, `awk`, etc. So given input `Yea=1,Yea=2` on the same input line, first `Yea=1` is stored, then updated with `Yea=2`. This is in the input-parser and the value `Yea=1` is unavailable to any further processing. The following example line comes from a headerless CSV file and includes 5 times the string (value) `'NA'`:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>ag '0.9' nas.csv | head -1</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
2:-349801.10097848,4537221.43295653,2,1,NA,NA,NA,NA,NA
|
||||
</pre>
|
||||
|
||||
The repeated ``'NA'`` strings (values) in the same line will be treated as fields (columns) with same name, thus only one is kept in the output.
|
||||
The repeated `'NA'` strings (values) in the same line will be treated as fields (columns) with same name, thus only one is kept in the output.
|
||||
|
||||
This can be worked around by telling ``mlr`` that there is no header row by using ``--implicit-csv-header`` or changing the input format by using ``nidx`` like so:
|
||||
This can be worked around by telling `mlr` that there is no header row by using `--implicit-csv-header` or changing the input format by using `nidx` like so:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
ag '0.9' nas.csv | mlr --n2c --fs "," label xsn,ysn,x,y,t,a,e29,e31,e32 then head
|
||||
</pre>
|
||||
|
||||
|
|
@ -94,15 +108,17 @@ ag '0.9' nas.csv | mlr --n2c --fs "," label xsn,ysn,x,y,t,a,e29,e31,e32 then hea
|
|||
|
||||
Miller handles compliant CSV: in particular, it's an error if the number of data fields in a given data line don't match the number of header lines. But in the event that you have a CSV file in which some lines have less than the full number of fields, you can use Miller to pad them out. The trick is to use NIDX format, for which each line stands on its own without respect to a header line.
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/ragged.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a,b,c
|
||||
1,2,3
|
||||
4,5
|
||||
6,7,8,9
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from data/ragged.csv --fs comma --nidx put '</b>
|
||||
<b> @maxnf = max(@maxnf, NF);</b>
|
||||
<b> @nf = NF;</b>
|
||||
|
|
@ -111,6 +127,8 @@ a,b,c
|
|||
<b> $[@nf] = ""</b>
|
||||
<b> }</b>
|
||||
<b>'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a,b,c
|
||||
1,2,3
|
||||
4,5
|
||||
|
|
@ -119,13 +137,15 @@ a,b,c
|
|||
|
||||
or, more simply,
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from data/ragged.csv --fs comma --nidx put '</b>
|
||||
<b> @maxnf = max(@maxnf, NF);</b>
|
||||
<b> while(NF < @maxnf) {</b>
|
||||
<b> $[NF+1] = "";</b>
|
||||
<b> }</b>
|
||||
<b>'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a,b,c
|
||||
1,2,3
|
||||
4,5
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
|
||||
## Headerless CSV on input or output
|
||||
|
||||
Sometimes we get CSV files which lack a header. For example (`data/headerless.csv <./data/headerless.csv>`_):
|
||||
Sometimes we get CSV files which lack a header. For example, [data/headerless.csv](./data/headerless.csv):
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
cat data/headerless.csv
|
||||
GENMD_EOF
|
||||
|
||||
You can use Miller to add a header. The ``--implicit-csv-header`` applies positionally indexed labels:
|
||||
You can use Miller to add a header. The `--implicit-csv-header` applies positionally indexed labels:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --csv --implicit-csv-header cat data/headerless.csv
|
||||
|
|
@ -20,7 +20,7 @@ GENMD_RUN_COMMAND
|
|||
mlr --csv --implicit-csv-header label name,age,status data/headerless.csv
|
||||
GENMD_EOF
|
||||
|
||||
Likewise, if you need to produce CSV which is lacking its header, you can pipe Miller's output to the system command ``sed 1d``, or you can use Miller's ``--headerless-csv-output`` option:
|
||||
Likewise, if you need to produce CSV which is lacking its header, you can pipe Miller's output to the system command `sed 1d`, or you can use Miller's `--headerless-csv-output` option:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
head -5 data/colored-shapes.dkvp | mlr --ocsv cat
|
||||
|
|
@ -38,16 +38,16 @@ GENMD_EOF
|
|||
|
||||
## Headerless CSV with duplicate field values
|
||||
|
||||
Miller is (by central design) a mapping from name to value, rather than integer position to value as in most tools in the Unix toolkit such as ``sort``, ``cut``, ``awk``, etc. So given input ``Yea=1,Yea=2`` on the same input line, first ``Yea=1`` is stored, then updated with ``Yea=2``. This is in the input-parser and the value ``Yea=1`` is unavailable to any further processing. The following example line comes from a headerless CSV file and includes 5 times the string (value) ``'NA'``:
|
||||
Miller is (by central design) a mapping from name to value, rather than integer position to value as in most tools in the Unix toolkit such as `sort`, `cut`, `awk`, etc. So given input `Yea=1,Yea=2` on the same input line, first `Yea=1` is stored, then updated with `Yea=2`. This is in the input-parser and the value `Yea=1` is unavailable to any further processing. The following example line comes from a headerless CSV file and includes 5 times the string (value) `'NA'`:
|
||||
|
||||
GENMD_CARDIFY_HIGHLIGHT_ONE
|
||||
ag '0.9' nas.csv | head -1
|
||||
2:-349801.10097848,4537221.43295653,2,1,NA,NA,NA,NA,NA
|
||||
GENMD_EOF
|
||||
|
||||
The repeated ``'NA'`` strings (values) in the same line will be treated as fields (columns) with same name, thus only one is kept in the output.
|
||||
The repeated `'NA'` strings (values) in the same line will be treated as fields (columns) with same name, thus only one is kept in the output.
|
||||
|
||||
This can be worked around by telling ``mlr`` that there is no header row by using ``--implicit-csv-header`` or changing the input format by using ``nidx`` like so:
|
||||
This can be worked around by telling `mlr` that there is no header row by using `--implicit-csv-header` or changing the input format by using `nidx` like so:
|
||||
|
||||
GENMD_CARDIFY
|
||||
ag '0.9' nas.csv | mlr --n2c --fs "," label xsn,ysn,x,y,t,a,e29,e31,e32 then head
|
||||
|
|
|
|||
|
|
@ -3,51 +3,51 @@
|
|||
|
||||
## How to use .mlrrc
|
||||
|
||||
Suppose you always use CSV files. Then instead of always having to type ``--csv`` as in
|
||||
Suppose you always use CSV files. Then instead of always having to type `--csv` as in
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --csv cut -x -f extra mydata.csv</b>
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --csv sort -n id mydata.csv</b>
|
||||
</pre>
|
||||
|
||||
and so on, you can instead put the following into your ``$HOME/.mlrrc``:
|
||||
and so on, you can instead put the following into your `$HOME/.mlrrc`:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
--csv
|
||||
</pre>
|
||||
|
||||
Then you can just type things like
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr cut -x -f extra mydata.csv</b>
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr sort -n id mydata.csv</b>
|
||||
</pre>
|
||||
|
||||
and the ``--csv`` part will automatically be understood. (If you do want to process, say, a JSON file then ``mlr --json ...`` at the command line will override the default from your ``.mlrrc``.)
|
||||
and the `--csv` part will automatically be understood. (If you do want to process, say, a JSON file then `mlr --json ...` at the command line will override the default from your `.mlrrc`.)
|
||||
|
||||
## What you can put in your .mlrrc
|
||||
|
||||
* You can include any command-line flags, except the "terminal" ones such as ``--help``.
|
||||
* You can include any command-line flags, except the "terminal" ones such as `--help`.
|
||||
|
||||
* The ``--prepipe``, ``--load``, and ``--mload`` flags aren't allowed in ``.mlrrc`` as they control code execution, and could result in your scripts running things you don't expect if you receive data from someone with a ``.mlrrc`` in it.
|
||||
* The `--prepipe`, `--load`, and `--mload` flags aren't allowed in `.mlrrc` as they control code execution, and could result in your scripts running things you don't expect if you receive data from someone with a `.mlrrc` in it.
|
||||
|
||||
* The formatting rule is you need to put one flag beginning with ``--`` per line: for example, ``--csv`` on one line and ``--nr-progress-mod 1000`` on a separate line.
|
||||
* The formatting rule is you need to put one flag beginning with `--` per line: for example, `--csv` on one line and `--nr-progress-mod 1000` on a separate line.
|
||||
|
||||
* Since every line starts with a ``--`` option, you can leave off the initial ``--`` if you want. For example, ``ojson`` is the same as ``--ojson``, and ``nr-progress-mod 1000`` is the same as ``--nr-progress-mod 1000``.
|
||||
* Since every line starts with a `--` option, you can leave off the initial `--` if you want. For example, `ojson` is the same as `--ojson`, and `nr-progress-mod 1000` is the same as `--nr-progress-mod 1000`.
|
||||
|
||||
* Comments are from a ``#`` to the end of the line.
|
||||
* Comments are from a `#` to the end of the line.
|
||||
|
||||
* Empty lines are ignored -- including lines which are empty after comments are removed.
|
||||
|
||||
Here is an example ``.mlrrc`` file:
|
||||
Here is an example `.mlrrc` file:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
# Input and output formats are CSV by default (unless otherwise specified
|
||||
# on the mlr command line):
|
||||
csv
|
||||
|
|
@ -67,20 +67,20 @@ skip-comments-with @
|
|||
|
||||
## Where to put your .mlrrc
|
||||
|
||||
If the environment variable ``MLRRC`` is set:
|
||||
If the environment variable `MLRRC` is set:
|
||||
|
||||
* If its value is ``__none__`` then no ``.mlrrc`` files are processed. (This is nice for things like regression testing.)
|
||||
* If its value is `__none__` then no `.mlrrc` files are processed. (This is nice for things like regression testing.)
|
||||
|
||||
* Otherwise, its value (as a filename) is loaded and processed. If there are syntax errors, they abort ``mlr`` with a usage message (as if you had mistyped something on the command line). If the file can't be loaded at all, though, it is silently skipped.
|
||||
* Otherwise, its value (as a filename) is loaded and processed. If there are syntax errors, they abort `mlr` with a usage message (as if you had mistyped something on the command line). If the file can't be loaded at all, though, it is silently skipped.
|
||||
|
||||
* Any ``.mlrrc`` in your home directory or current directory is ignored whenever ``MLRRC`` is set in the environment.
|
||||
* Any `.mlrrc` in your home directory or current directory is ignored whenever `MLRRC` is set in the environment.
|
||||
|
||||
* Example line in your shell's rc file: ``export MLRRC=/path/to/my/mlrrc``
|
||||
* Example line in your shell's rc file: `export MLRRC=/path/to/my/mlrrc`
|
||||
|
||||
Otherwise:
|
||||
|
||||
* If ``$HOME/.mlrrc`` exists, it's processed as above.
|
||||
* If `$HOME/.mlrrc` exists, it's processed as above.
|
||||
|
||||
* If ``./.mlrrc`` exists, it's then also processed as above.
|
||||
* If `./.mlrrc` exists, it's then also processed as above.
|
||||
|
||||
* The idea is you can have all your settings in your ``$HOME/.mlrrc``, then override maybe one or two for your current directory if you like.
|
||||
* The idea is you can have all your settings in your `$HOME/.mlrrc`, then override maybe one or two for your current directory if you like.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
## How to use .mlrrc
|
||||
|
||||
Suppose you always use CSV files. Then instead of always having to type ``--csv`` as in
|
||||
Suppose you always use CSV files. Then instead of always having to type `--csv` as in
|
||||
|
||||
GENMD_CARDIFY_HIGHLIGHT_ONE
|
||||
mlr --csv cut -x -f extra mydata.csv
|
||||
|
|
@ -12,7 +12,7 @@ GENMD_CARDIFY_HIGHLIGHT_ONE
|
|||
mlr --csv sort -n id mydata.csv
|
||||
GENMD_EOF
|
||||
|
||||
and so on, you can instead put the following into your ``$HOME/.mlrrc``:
|
||||
and so on, you can instead put the following into your `$HOME/.mlrrc`:
|
||||
|
||||
GENMD_CARDIFY
|
||||
--csv
|
||||
|
|
@ -28,42 +28,42 @@ GENMD_CARDIFY_HIGHLIGHT_ONE
|
|||
mlr sort -n id mydata.csv
|
||||
GENMD_EOF
|
||||
|
||||
and the ``--csv`` part will automatically be understood. (If you do want to process, say, a JSON file then ``mlr --json ...`` at the command line will override the default from your ``.mlrrc``.)
|
||||
and the `--csv` part will automatically be understood. (If you do want to process, say, a JSON file then `mlr --json ...` at the command line will override the default from your `.mlrrc`.)
|
||||
|
||||
## What you can put in your .mlrrc
|
||||
|
||||
* You can include any command-line flags, except the "terminal" ones such as ``--help``.
|
||||
* You can include any command-line flags, except the "terminal" ones such as `--help`.
|
||||
|
||||
* The ``--prepipe``, ``--load``, and ``--mload`` flags aren't allowed in ``.mlrrc`` as they control code execution, and could result in your scripts running things you don't expect if you receive data from someone with a ``.mlrrc`` in it.
|
||||
* The `--prepipe`, `--load`, and `--mload` flags aren't allowed in `.mlrrc` as they control code execution, and could result in your scripts running things you don't expect if you receive data from someone with a `.mlrrc` in it.
|
||||
|
||||
* The formatting rule is you need to put one flag beginning with ``--`` per line: for example, ``--csv`` on one line and ``--nr-progress-mod 1000`` on a separate line.
|
||||
* The formatting rule is you need to put one flag beginning with `--` per line: for example, `--csv` on one line and `--nr-progress-mod 1000` on a separate line.
|
||||
|
||||
* Since every line starts with a ``--`` option, you can leave off the initial ``--`` if you want. For example, ``ojson`` is the same as ``--ojson``, and ``nr-progress-mod 1000`` is the same as ``--nr-progress-mod 1000``.
|
||||
* Since every line starts with a `--` option, you can leave off the initial `--` if you want. For example, `ojson` is the same as `--ojson`, and `nr-progress-mod 1000` is the same as `--nr-progress-mod 1000`.
|
||||
|
||||
* Comments are from a ``#`` to the end of the line.
|
||||
* Comments are from a `#` to the end of the line.
|
||||
|
||||
* Empty lines are ignored -- including lines which are empty after comments are removed.
|
||||
|
||||
Here is an example ``.mlrrc`` file:
|
||||
Here is an example `.mlrrc` file:
|
||||
|
||||
GENMD_INCLUDE_ESCAPED(sample_mlrrc)
|
||||
|
||||
## Where to put your .mlrrc
|
||||
|
||||
If the environment variable ``MLRRC`` is set:
|
||||
If the environment variable `MLRRC` is set:
|
||||
|
||||
* If its value is ``__none__`` then no ``.mlrrc`` files are processed. (This is nice for things like regression testing.)
|
||||
* If its value is `__none__` then no `.mlrrc` files are processed. (This is nice for things like regression testing.)
|
||||
|
||||
* Otherwise, its value (as a filename) is loaded and processed. If there are syntax errors, they abort ``mlr`` with a usage message (as if you had mistyped something on the command line). If the file can't be loaded at all, though, it is silently skipped.
|
||||
* Otherwise, its value (as a filename) is loaded and processed. If there are syntax errors, they abort `mlr` with a usage message (as if you had mistyped something on the command line). If the file can't be loaded at all, though, it is silently skipped.
|
||||
|
||||
* Any ``.mlrrc`` in your home directory or current directory is ignored whenever ``MLRRC`` is set in the environment.
|
||||
* Any `.mlrrc` in your home directory or current directory is ignored whenever `MLRRC` is set in the environment.
|
||||
|
||||
* Example line in your shell's rc file: ``export MLRRC=/path/to/my/mlrrc``
|
||||
* Example line in your shell's rc file: `export MLRRC=/path/to/my/mlrrc`
|
||||
|
||||
Otherwise:
|
||||
|
||||
* If ``$HOME/.mlrrc`` exists, it's processed as above.
|
||||
* If `$HOME/.mlrrc` exists, it's processed as above.
|
||||
|
||||
* If ``./.mlrrc`` exists, it's then also processed as above.
|
||||
* If `./.mlrrc` exists, it's then also processed as above.
|
||||
|
||||
* The idea is you can have all your settings in your ``$HOME/.mlrrc``, then override maybe one or two for your current directory if you like.
|
||||
* The idea is you can have all your settings in your `$HOME/.mlrrc`, then override maybe one or two for your current directory if you like.
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
<!--- PLEASE DO NOT EDIT DIRECTLY. EDIT THE .md.in FILE PLEASE. --->
|
||||
# Data-cleaning examples
|
||||
|
||||
Here are some ways to use the type-checking options as described in :ref:`reference-dsl-type-tests-and-assertions` Suppose you have the following data file, with inconsistent typing for boolean. (Also imagine that, for the sake of discussion, we have a million-line file rather than a four-line file, so we can't see it all at once and some automation is called for.)
|
||||
Here are some ways to use the type-checking options as described in [Type-checking](reference-dsl-variables.md#type-checking). Suppose you have the following data file, with inconsistent typing for boolean. (Also imagine that, for the sake of discussion, we have a million-line file rather than a four-line file, so we can't see it all at once and some automation is called for.)
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/het-bool.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
name,reachable
|
||||
barney,false
|
||||
betty,true
|
||||
|
|
@ -14,8 +16,10 @@ wilma,1
|
|||
|
||||
One option is to coerce everything to boolean, or integer:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsv --opprint put '$reachable = boolean($reachable)' data/het-bool.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
name reachable
|
||||
barney false
|
||||
betty true
|
||||
|
|
@ -23,8 +27,10 @@ fred true
|
|||
wilma true
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsv --opprint put '$reachable = int(boolean($reachable))' data/het-bool.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
name reachable
|
||||
barney 0
|
||||
betty 1
|
||||
|
|
@ -34,8 +40,10 @@ wilma 1
|
|||
|
||||
A second option is to flag badly formatted data within the output stream:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsv --opprint put '$format_ok = is_string($reachable)' data/het-bool.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
name reachable format_ok
|
||||
barney false false
|
||||
betty true false
|
||||
|
|
@ -45,10 +53,12 @@ wilma 1 false
|
|||
|
||||
Or perhaps to flag badly formatted data outside the output stream:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsv --opprint put '</b>
|
||||
<b> if (!is_string($reachable)) {eprint "Malformed at NR=".NR}</b>
|
||||
<b>' data/het-bool.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
Malformed at NR=1
|
||||
Malformed at NR=2
|
||||
Malformed at NR=3
|
||||
|
|
@ -62,7 +72,9 @@ wilma 1
|
|||
|
||||
A third way is to abort the process on fimd.instance of bad data:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --csv put '$reachable = asserting_string($reachable)' data/het-bool.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
Miller: is_string type-assertion failed at NR=1 FNR=1 FILENAME=data/het-bool.csv
|
||||
</pre>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Data-cleaning examples
|
||||
|
||||
Here are some ways to use the type-checking options as described in :ref:`reference-dsl-type-tests-and-assertions` Suppose you have the following data file, with inconsistent typing for boolean. (Also imagine that, for the sake of discussion, we have a million-line file rather than a four-line file, so we can't see it all at once and some automation is called for.)
|
||||
Here are some ways to use the type-checking options as described in [Type-checking](reference-dsl-variables.md#type-checking). Suppose you have the following data file, with inconsistent typing for boolean. (Also imagine that, for the sake of discussion, we have a million-line file rather than a four-line file, so we can't see it all at once and some automation is called for.)
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
cat data/het-bool.csv
|
||||
|
|
|
|||
|
|
@ -7,8 +7,10 @@ The [flins.csv](data/flins.csv) file is some sample data obtained from [https://
|
|||
|
||||
Vertical-tabular format is good for a quick look at CSV data layout -- seeing what columns you have to work with:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>head -n 2 data/flins.csv | mlr --icsv --oxtab cat</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
county Seminole
|
||||
tiv_2011 22890.55
|
||||
tiv_2012 20848.71
|
||||
|
|
@ -17,8 +19,10 @@ line Residential
|
|||
|
||||
A few simple queries:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from data/flins.csv --icsv --opprint count-distinct -f county | head</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
county count
|
||||
Seminole 1
|
||||
Miami Dade 2
|
||||
|
|
@ -28,32 +32,36 @@ Duval 1
|
|||
St. Johns 1
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from data/flins.csv --icsv --opprint count-distinct -f construction,line</b>
|
||||
</pre>
|
||||
|
||||
Categorization of total insured value:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from data/flins.csv --icsv --opprint stats1 -a min,mean,max -f tiv_2012</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
tiv_2012_min tiv_2012_mean tiv_2012_max
|
||||
19757.91 1061531.4637499999 2785551.63
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from data/flins.csv --icsv --opprint \</b>
|
||||
<b> stats1 -a min,mean,max -f tiv_2012 -g construction,line</b>
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from data/flins.csv --icsv --oxtab \</b>
|
||||
<b> stats1 -a p0,p10,p50,p90,p95,p99,p100 -f hu_site_deductible</b>
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from data/flins.csv --icsv --opprint \</b>
|
||||
<b> stats1 -a p95,p99,p100 -f hu_site_deductible -g county \</b>
|
||||
<b> then sort -f county | head</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
county
|
||||
Duval
|
||||
Highlands
|
||||
|
|
@ -63,9 +71,11 @@ Seminole
|
|||
St. Johns
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from data/flins.csv --icsv --oxtab \</b>
|
||||
<b> stats2 -a corr,linreg-ols,r2 -f tiv_2011,tiv_2012</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
tiv_2011_tiv_2012_corr 0.9353629581411828
|
||||
tiv_2011_tiv_2012_ols_m 1.0890905877734807
|
||||
tiv_2011_tiv_2012_ols_b 103095.52335638746
|
||||
|
|
@ -73,9 +83,11 @@ tiv_2011_tiv_2012_ols_n 8
|
|||
tiv_2011_tiv_2012_r2 0.8749038634626236
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from data/flins.csv --icsv --opprint \</b>
|
||||
<b> stats2 -a corr,linreg-ols,r2 -f tiv_2011,tiv_2012 -g county</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
county tiv_2011_tiv_2012_corr tiv_2011_tiv_2012_ols_m tiv_2011_tiv_2012_ols_b tiv_2011_tiv_2012_ols_n tiv_2011_tiv_2012_r2
|
||||
Seminole - - - 1 -
|
||||
Miami Dade 1 0.9306426512386247 -2311.1543275160047 2 0.9999999999999999
|
||||
|
|
@ -87,25 +99,29 @@ St. Johns - - -
|
|||
|
||||
## Color/shape data
|
||||
|
||||
The [data/colored-shapes.dkvp](data/colored-shapes.dkvp) file is some sample data produced by the [mkdat2](data/mkdat2) script. The idea is:
|
||||
The [data/colored-shapes.dkvp](data/colored-shapes.dkvp) file is some sample data produced by the [mkdat2](../data/mkdat2) script. The idea is:
|
||||
|
||||
* Produce some data with known distributions and correlations, and verify that Miller recovers those properties empirically.
|
||||
* Each record is labeled with one of a few colors and one of a few shapes.
|
||||
* The ``flag`` field is 0 or 1, with probability dependent on color
|
||||
* The ``u`` field is plain uniform on the unit interval.
|
||||
* The ``v`` field is the same, except tightly correlated with ``u`` for red circles.
|
||||
* The ``w`` field is autocorrelated for each color/shape pair.
|
||||
* The ``x`` field is boring Gaussian with mean 5 and standard deviation about 1.2, with no dependence on color or shape.
|
||||
* The `flag` field is 0 or 1, with probability dependent on color
|
||||
* The `u` field is plain uniform on the unit interval.
|
||||
* The `v` field is the same, except tightly correlated with `u` for red circles.
|
||||
* The `w` field is autocorrelated for each color/shape pair.
|
||||
* The `x` field is boring Gaussian with mean 5 and standard deviation about 1.2, with no dependence on color or shape.
|
||||
|
||||
Peek at the data:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>wc -l data/colored-shapes.dkvp</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
10078 data/colored-shapes.dkvp
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>head -n 6 data/colored-shapes.dkvp | mlr --opprint cat</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color shape flag i u v w x
|
||||
yellow triangle 1 11 0.6321695890307647 0.9887207810889004 0.4364983936735774 5.7981881667050565
|
||||
red square 1 15 0.21966833570651523 0.001257332190235938 0.7927778364718627 2.944117399716207
|
||||
|
|
@ -117,10 +133,12 @@ red square 0 64 0.2015510269821953 0.9531098083420033 0.7719912015786
|
|||
|
||||
Look at uncategorized stats (using [creach](https://github.com/johnkerl/scripts/blob/master/fundam/creach) for spacing).
|
||||
|
||||
Here it looks reasonable that ``u`` is unit-uniform; something's up with ``v`` but we can't yet see what:
|
||||
Here it looks reasonable that `u` is unit-uniform; something's up with `v` but we can't yet see what:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --oxtab stats1 -a min,mean,max -f flag,u,v data/colored-shapes.dkvp | creach 3</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
flag_min 0
|
||||
flag_mean 0.39888866838658465
|
||||
flag_max 1
|
||||
|
|
@ -136,8 +154,10 @@ v_max 1.0724998185026013
|
|||
|
||||
The histogram shows the different distribution of 0/1 flags:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --opprint histogram -f flag,u,v --lo -0.1 --hi 1.1 --nbins 12 data/colored-shapes.dkvp</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
bin_lo bin_hi flag_count u_count v_count
|
||||
-0.010000000000000002 0.09000000000000002 6058 0 36
|
||||
0.09000000000000002 0.19000000000000003 0 1062 988
|
||||
|
|
@ -155,10 +175,12 @@ bin_lo bin_hi flag_count u_count v_count
|
|||
|
||||
Look at univariate stats by color and shape. In particular, color-dependent flag probabilities pop out, aligning with their original Bernoulli probablities from the data-generator script:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --opprint stats1 -a min,mean,max -f flag,u,v -g color \</b>
|
||||
<b> then sort -f color \</b>
|
||||
<b> data/colored-shapes.dkvp</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color flag_min flag_mean flag_max u_min u_mean u_max v_min v_mean v_max
|
||||
blue 0 0.5843537414965987 1 0.000043912454007477564 0.517717155039078 0.9999687954968421 0.0014886830387470518 0.49105642841387653 0.9995761761685742
|
||||
green 0 0.20919747520288548 1 0.00048750676198217047 0.5048610622924616 0.9999361779701204 0.0005012669003675585 0.49908475928072205 0.9996764373885353
|
||||
|
|
@ -168,28 +190,34 @@ red 0 0.3031674208144796 1 0.0006711367180041172 0.492559648
|
|||
yellow 0 0.8924274593064402 1 0.001300228762057487 0.49712912165196765 0.99992313390574 0.0007109695568577878 0.510626599360317 0.9999189897724752
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --opprint stats1 -a min,mean,max -f flag,u,v -g shape \</b>
|
||||
<b> then sort -f shape \</b>
|
||||
<b> data/colored-shapes.dkvp</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
shape flag_min flag_mean flag_max u_min u_mean u_max v_min v_mean v_max
|
||||
circle 0 0.3998456194519491 1 0.000043912454007477564 0.49855450951394115 0.99992313390574 -0.09270905318501277 0.49552415740048406 1.0724998185026013
|
||||
square 0 0.39611178614823817 1 0.0001881939925673093 0.499385458061097 0.9999687954968421 0.00008930277299445954 0.49653825501903986 0.9999751864255598
|
||||
triangle 0 0.4015421115065243 1 0.000881025170573424 0.4968585405884252 0.9996614910922645 0.000716883409890845 0.501049532862137 0.9999946837499262
|
||||
</pre>
|
||||
|
||||
Look at bivariate stats by color and shape. In particular, ``u,v`` pairwise correlation for red circles pops out:
|
||||
Look at bivariate stats by color and shape. In particular, `u,v` pairwise correlation for red circles pops out:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --opprint --right stats2 -a corr -f u,v,w,x data/colored-shapes.dkvp</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
u_v_corr w_x_corr
|
||||
0.13341803768384553 -0.011319938208638764
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --opprint --right \</b>
|
||||
<b> stats2 -a corr -f u,v,w,x -g color,shape then sort -nr u_v_corr \</b>
|
||||
<b> data/colored-shapes.dkvp</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color shape u_v_corr w_x_corr
|
||||
red circle 0.9807984157534667 -0.018565046320623148
|
||||
orange square 0.17685846147882145 -0.07104374629148885
|
||||
|
|
|
|||
|
|
@ -54,15 +54,15 @@ GENMD_EOF
|
|||
|
||||
## Color/shape data
|
||||
|
||||
The [data/colored-shapes.dkvp](data/colored-shapes.dkvp) file is some sample data produced by the [mkdat2](data/mkdat2) script. The idea is:
|
||||
The [data/colored-shapes.dkvp](data/colored-shapes.dkvp) file is some sample data produced by the [mkdat2](../data/mkdat2) script. The idea is:
|
||||
|
||||
* Produce some data with known distributions and correlations, and verify that Miller recovers those properties empirically.
|
||||
* Each record is labeled with one of a few colors and one of a few shapes.
|
||||
* The ``flag`` field is 0 or 1, with probability dependent on color
|
||||
* The ``u`` field is plain uniform on the unit interval.
|
||||
* The ``v`` field is the same, except tightly correlated with ``u`` for red circles.
|
||||
* The ``w`` field is autocorrelated for each color/shape pair.
|
||||
* The ``x`` field is boring Gaussian with mean 5 and standard deviation about 1.2, with no dependence on color or shape.
|
||||
* The `flag` field is 0 or 1, with probability dependent on color
|
||||
* The `u` field is plain uniform on the unit interval.
|
||||
* The `v` field is the same, except tightly correlated with `u` for red circles.
|
||||
* The `w` field is autocorrelated for each color/shape pair.
|
||||
* The `x` field is boring Gaussian with mean 5 and standard deviation about 1.2, with no dependence on color or shape.
|
||||
|
||||
Peek at the data:
|
||||
|
||||
|
|
@ -76,7 +76,7 @@ GENMD_EOF
|
|||
|
||||
Look at uncategorized stats (using [creach](https://github.com/johnkerl/scripts/blob/master/fundam/creach) for spacing).
|
||||
|
||||
Here it looks reasonable that ``u`` is unit-uniform; something's up with ``v`` but we can't yet see what:
|
||||
Here it looks reasonable that `u` is unit-uniform; something's up with `v` but we can't yet see what:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --oxtab stats1 -a min,mean,max -f flag,u,v data/colored-shapes.dkvp | creach 3
|
||||
|
|
@ -102,7 +102,7 @@ mlr --opprint stats1 -a min,mean,max -f flag,u,v -g shape \
|
|||
data/colored-shapes.dkvp
|
||||
GENMD_EOF
|
||||
|
||||
Look at bivariate stats by color and shape. In particular, ``u,v`` pairwise correlation for red circles pops out:
|
||||
Look at bivariate stats by color and shape. In particular, `u,v` pairwise correlation for red circles pops out:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --opprint --right stats2 -a corr -f u,v,w,x data/colored-shapes.dkvp
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
for (k in $*) {
|
||||
$[k] = gsub($[k], "e", "X");
|
||||
}
|
||||
for (k in $*) {
|
||||
$[k] = gsub($[k], "e", "X");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,32 +5,38 @@
|
|||
|
||||
Given input like
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat dates.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
date,event
|
||||
2018-02-03,initialization
|
||||
2018-03-07,discovery
|
||||
2018-02-03,allocation
|
||||
</pre>
|
||||
|
||||
we can use ``strptime`` to parse the date field into seconds-since-epoch and then do numeric comparisons. Simply match your input dataset's date-formatting to the :ref:`reference-dsl-strptime` format-string. For example:
|
||||
we can use [strptime](reference-verbs.md#strptime) to parse the date field into seconds-since-epoch and then do numeric comparisons. Simply match your input dataset's date-formatting to the [strptime](reference-verbs.md#strptime) format-string. For example:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --csv filter '</b>
|
||||
<b> strptime($date, "%Y-%m-%d") > strptime("2018-03-03", "%Y-%m-%d")</b>
|
||||
<b>' dates.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
date,event
|
||||
2018-03-07,discovery
|
||||
</pre>
|
||||
|
||||
Caveat: localtime-handling in timezones with DST is still a work in progress; see https://github.com/johnkerl/miller/issues/170. See also https://github.com/johnkerl/miller/issues/208 -- thanks @aborruso!
|
||||
Caveat: localtime-handling in timezones with DST is still a work in progress; see [https://github.com/johnkerl/miller/issues/170](https://github.com/johnkerl/miller/issues/170) . See also [https://github.com/johnkerl/miller/issues/208](https://github.com/johnkerl/miller/issues/208) -- thanks @aborruso!
|
||||
|
||||
## Finding missing dates
|
||||
|
||||
Suppose you have some date-stamped data which may (or may not) be missing entries for one or more dates:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>head -n 10 data/miss-date.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
date,qoh
|
||||
2012-03-05,10055
|
||||
2012-03-06,10486
|
||||
|
|
@ -43,19 +49,23 @@ date,qoh
|
|||
2012-03-13,11177
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>wc -l data/miss-date.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
1372 data/miss-date.csv
|
||||
</pre>
|
||||
|
||||
Since there are 1372 lines in the data file, some automation is called for. To find the missing dates, you can convert the dates to seconds since the epoch using ``strptime``, then compute adjacent differences (the ``cat -n`` simply inserts record-counters):
|
||||
Since there are 1372 lines in the data file, some automation is called for. To find the missing dates, you can convert the dates to seconds since the epoch using `strptime`, then compute adjacent differences (the `cat -n` simply inserts record-counters):
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from data/miss-date.csv --icsv \</b>
|
||||
<b> cat -n \</b>
|
||||
<b> then put '$datestamp = strptime($date, "%Y-%m-%d")' \</b>
|
||||
<b> then step -a delta -f datestamp \</b>
|
||||
<b>| head</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
n=1,date=2012-03-05,qoh=10055,datestamp=1330905600,datestamp_delta=0
|
||||
n=2,date=2012-03-06,qoh=10486,datestamp=1330992000,datestamp_delta=86400
|
||||
n=3,date=2012-03-07,qoh=10430,datestamp=1331078400,datestamp_delta=86400
|
||||
|
|
@ -70,20 +80,24 @@ n=10,date=2012-03-14,qoh=11498,datestamp=1331683200,datestamp_delta=86400
|
|||
|
||||
Then, filter for adjacent difference not being 86400 (the number of seconds in a day):
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from data/miss-date.csv --icsv \</b>
|
||||
<b> cat -n \</b>
|
||||
<b> then put '$datestamp = strptime($date, "%Y-%m-%d")' \</b>
|
||||
<b> then step -a delta -f datestamp \</b>
|
||||
<b> then filter '$datestamp_delta != 86400 && $n != 1'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
n=774,date=2014-04-19,qoh=130140,datestamp=1397865600,datestamp_delta=259200
|
||||
n=1119,date=2015-03-31,qoh=181625,datestamp=1427760000,datestamp_delta=172800
|
||||
</pre>
|
||||
|
||||
Given this, it's now easy to see where the gaps are:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr cat -n then filter '$n >= 770 && $n <= 780' data/miss-date.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
n=770,1=2014-04-12,2=129435
|
||||
n=771,1=2014-04-13,2=129868
|
||||
n=772,1=2014-04-14,2=129797
|
||||
|
|
@ -97,8 +111,10 @@ n=779,1=2014-04-23,2=130849
|
|||
n=780,1=2014-04-24,2=131026
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr cat -n then filter '$n >= 1115 && $n <= 1125' data/miss-date.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
n=1115,1=2015-03-25,2=181006
|
||||
n=1116,1=2015-03-26,2=180995
|
||||
n=1117,1=2015-03-27,2=181043
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ GENMD_RUN_COMMAND
|
|||
cat dates.csv
|
||||
GENMD_EOF
|
||||
|
||||
we can use ``strptime`` to parse the date field into seconds-since-epoch and then do numeric comparisons. Simply match your input dataset's date-formatting to the :ref:`reference-dsl-strptime` format-string. For example:
|
||||
we can use [strptime](reference-verbs.md#strptime) to parse the date field into seconds-since-epoch and then do numeric comparisons. Simply match your input dataset's date-formatting to the [strptime](reference-verbs.md#strptime) format-string. For example:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --csv filter '
|
||||
|
|
@ -16,7 +16,7 @@ mlr --csv filter '
|
|||
' dates.csv
|
||||
GENMD_EOF
|
||||
|
||||
Caveat: localtime-handling in timezones with DST is still a work in progress; see https://github.com/johnkerl/miller/issues/170. See also https://github.com/johnkerl/miller/issues/208 -- thanks @aborruso!
|
||||
Caveat: localtime-handling in timezones with DST is still a work in progress; see [https://github.com/johnkerl/miller/issues/170](https://github.com/johnkerl/miller/issues/170) . See also [https://github.com/johnkerl/miller/issues/208](https://github.com/johnkerl/miller/issues/208) -- thanks @aborruso!
|
||||
|
||||
## Finding missing dates
|
||||
|
||||
|
|
@ -30,7 +30,7 @@ GENMD_RUN_COMMAND
|
|||
wc -l data/miss-date.csv
|
||||
GENMD_EOF
|
||||
|
||||
Since there are 1372 lines in the data file, some automation is called for. To find the missing dates, you can convert the dates to seconds since the epoch using ``strptime``, then compute adjacent differences (the ``cat -n`` simply inserts record-counters):
|
||||
Since there are 1372 lines in the data file, some automation is called for. To find the missing dates, you can convert the dates to seconds since the epoch using `strptime`, then compute adjacent differences (the `cat -n` simply inserts record-counters):
|
||||
|
||||
GENMD_INCLUDE_AND_RUN_ESCAPED(data/miss-date-1.sh)
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
Here are the I/O routines:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
#!/usr/bin/env python
|
||||
|
||||
# ================================================================
|
||||
|
|
@ -68,8 +68,10 @@ def map2dkvpline(map , ops, ofs):
|
|||
|
||||
And here is an example using them:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat polyglot-dkvp-io/example.py</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
#!/usr/bin/env python
|
||||
|
||||
import sys
|
||||
|
|
@ -108,8 +110,10 @@ while True:
|
|||
|
||||
Run as-is:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>python polyglot-dkvp-io/example.py < data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,b=pan,i=1,y=0.7268028627434533,ab=panpan,iy=1.7268028627434533,ta=str,tb=str,ti=int,ty=float,tab=str,tiy=float
|
||||
a=eks,b=pan,i=2,y=0.5221511083334797,ab=ekspan,iy=2.5221511083334796,ta=str,tb=str,ti=int,ty=float,tab=str,tiy=float
|
||||
a=wye,b=wye,i=3,y=0.33831852551664776,ab=wyewye,iy=3.3383185255166477,ta=str,tb=str,ti=int,ty=float,tab=str,tiy=float
|
||||
|
|
@ -119,8 +123,10 @@ a=wye,b=pan,i=5,y=0.8636244699032729,ab=wyepan,iy=5.863624469903273,ta=str,tb=st
|
|||
|
||||
Run as-is, then pipe to Miller for pretty-printing:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>python polyglot-dkvp-io/example.py < data/small | mlr --opprint cat</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a b i y ab iy ta tb ti ty tab tiy
|
||||
pan pan 1 0.7268028627434533 panpan 1.7268028627434533 str str int float str float
|
||||
eks pan 2 0.5221511083334797 ekspan 2.5221511083334796 str str int float str float
|
||||
|
|
@ -133,7 +139,7 @@ wye pan 5 0.8636244699032729 wyepan 5.863624469903273 str str int float str fl
|
|||
|
||||
Here are the I/O routines:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
# ================================================================
|
||||
|
|
@ -190,8 +196,10 @@ end
|
|||
|
||||
And here is an example using them:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat polyglot-dkvp-io/example.rb</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require 'dkvp_io'
|
||||
|
|
@ -220,8 +228,10 @@ end
|
|||
|
||||
Run as-is:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>ruby -I./polyglot-dkvp-io polyglot-dkvp-io/example.rb data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,b=pan,i=1,y=0.7268028627434533,ab=panpan,iy=1.7268028627434533,ta=String,tb=String,ti=Integer,ty=Float,tab=String,tiy=Float
|
||||
a=eks,b=pan,i=2,y=0.5221511083334797,ab=ekspan,iy=2.5221511083334796,ta=String,tb=String,ti=Integer,ty=Float,tab=String,tiy=Float
|
||||
a=wye,b=wye,i=3,y=0.33831852551664776,ab=wyewye,iy=3.3383185255166477,ta=String,tb=String,ti=Integer,ty=Float,tab=String,tiy=Float
|
||||
|
|
@ -231,8 +241,10 @@ a=wye,b=pan,i=5,y=0.8636244699032729,ab=wyepan,iy=5.863624469903273,ta=String,tb
|
|||
|
||||
Run as-is, then pipe to Miller for pretty-printing:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>ruby -I./polyglot-dkvp-io polyglot-dkvp-io/example.rb data/small | mlr --opprint cat</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a b i y ab iy ta tb ti ty tab tiy
|
||||
pan pan 1 0.7268028627434533 panpan 1.7268028627434533 String String Integer Float String Float
|
||||
eks pan 2 0.5221511083334797 ekspan 2.5221511083334796 String String Integer Float String Float
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<!--- PLEASE DO NOT EDIT DIRECTLY. EDIT THE .md.in FILE PLEASE. --->
|
||||
# Why call it Miller?
|
||||
|
||||
The Unix toolkit was created in the **1970s** and is a mainstay to this day. Miller's look and feel adheres closely to the [classic toolkit style](http://en.wikipedia.org/wiki/Unix_philosophy): if this were music, Miller would be a **tribute album**. Likewise, since commands are subcommands of the ``mlr`` executable, the result is a **band**, if you will, of command-line tools. Put these together and the namesake is another classic product of the 1970s: the [Steve Miller Band](http://en.wikipedia.org/wiki/Steve%5fMiller%5fBand).
|
||||
The Unix toolkit was created in the **1970s** and is a mainstay to this day. Miller's look and feel adheres closely to the [classic toolkit style](http://en.wikipedia.org/wiki/Unix_philosophy): if this were music, Miller would be a **tribute album**. Likewise, since commands are subcommands of the `mlr` executable, the result is a **band**, if you will, of command-line tools. Put these together and the namesake is another classic product of the 1970s: the [Steve Miller Band](http://en.wikipedia.org/wiki/Steve%5fMiller%5fBand).
|
||||
|
||||
(Additionally, and far more prosaically ... just as a miller is someone who grinds and mixes grain into flour to extend its usefulness, Miller grinds and mixes data for you.)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# Why call it Miller?
|
||||
|
||||
The Unix toolkit was created in the **1970s** and is a mainstay to this day. Miller's look and feel adheres closely to the [classic toolkit style](http://en.wikipedia.org/wiki/Unix_philosophy): if this were music, Miller would be a **tribute album**. Likewise, since commands are subcommands of the ``mlr`` executable, the result is a **band**, if you will, of command-line tools. Put these together and the namesake is another classic product of the 1970s: the [Steve Miller Band](http://en.wikipedia.org/wiki/Steve%5fMiller%5fBand).
|
||||
The Unix toolkit was created in the **1970s** and is a mainstay to this day. Miller's look and feel adheres closely to the [classic toolkit style](http://en.wikipedia.org/wiki/Unix_philosophy): if this were music, Miller would be a **tribute album**. Likewise, since commands are subcommands of the `mlr` executable, the result is a **band**, if you will, of command-line tools. Put these together and the namesake is another classic product of the 1970s: the [Steve Miller Band](http://en.wikipedia.org/wiki/Steve%5fMiller%5fBand).
|
||||
|
||||
(Additionally, and far more prosaically ... just as a miller is someone who grinds and mixes grain into flour to extend its usefulness, Miller grinds and mixes data for you.)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,17 @@
|
|||
/* Top left, desktop */
|
||||
.wy-menu {
|
||||
background-color: #c0c0c0;
|
||||
/*background-color: #404040;*/
|
||||
}
|
||||
/* Top left, mobile */
|
||||
.wy-nav-top {
|
||||
background-color: #c0c0c0;
|
||||
}
|
||||
/* Left navbar */
|
||||
.wy-side-nav-search {
|
||||
background-color: #c0c0c0;
|
||||
/*background-color: #404040;*/
|
||||
}
|
||||
|
||||
/* Code samples without <pre> (none currently in use) */
|
||||
.rst-content code {
|
||||
color: #000000;
|
||||
background-color: #e0e0e0;
|
||||
|
|
@ -18,11 +24,27 @@
|
|||
color: #0FF1CE;
|
||||
}
|
||||
.caption-text {
|
||||
/*background-color: #808080; */
|
||||
color: maroon;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
/* Code samples using <pre> (which is currently in use) */
|
||||
.pre-highlight {
|
||||
color: #000000;
|
||||
background-color: #c5b690;
|
||||
font-size: 12px;
|
||||
padding: 10px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
.pre-non-highlight {
|
||||
color: #000000;
|
||||
background-color: #eae2cb;
|
||||
font-size: 12px;
|
||||
padding: 10px;
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
/* Section titles in content pages */
|
||||
h1 {
|
||||
color: maroon;
|
||||
background-color: #e0e0e0;
|
||||
|
|
@ -36,31 +58,7 @@ h3 {
|
|||
background-color: #e0e0e0;
|
||||
}
|
||||
|
||||
pre {
|
||||
color: #000000;
|
||||
background-color: #eae2cb;
|
||||
font-size: 12px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
pre b {
|
||||
background-color: #c5b690;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: orange;
|
||||
}
|
||||
a:visited {
|
||||
color: green;
|
||||
}
|
||||
a:hover {
|
||||
color: blue;
|
||||
}
|
||||
a .internal {
|
||||
color: red;
|
||||
}
|
||||
|
||||
/* Hyperlinks in content pages */
|
||||
a {
|
||||
color: maroon;
|
||||
}
|
||||
|
|
@ -72,6 +70,7 @@ a:hover {
|
|||
color: maroon;
|
||||
}
|
||||
|
||||
/* Hyperlinks in navbar */
|
||||
.wy-menu-vertical li ul li a {
|
||||
margin-bottom:0;
|
||||
color:#000000;
|
||||
|
|
|
|||
|
|
@ -1,61 +1,69 @@
|
|||
<!--- PLEASE DO NOT EDIT DIRECTLY. EDIT THE .md.in FILE PLEASE. --->
|
||||
# Unix-toolkit context
|
||||
|
||||
How does Miller fit within the Unix toolkit (``grep``, ``sed``, ``awk``, etc.)?
|
||||
How does Miller fit within the Unix toolkit (`grep`, `sed`, `awk`, etc.)?
|
||||
|
||||
## File-format awareness
|
||||
|
||||
Miller respects CSV headers. If you do ``mlr --csv cat *.csv`` then the header line is written once:
|
||||
Miller respects CSV headers. If you do `mlr --csv cat *.csv` then the header line is written once:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/a.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a,b,c
|
||||
1,2,3
|
||||
4,5,6
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/b.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a,b,c
|
||||
7,8,9
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --csv cat data/a.csv data/b.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a,b,c
|
||||
1,2,3
|
||||
4,5,6
|
||||
7,8,9
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --csv sort -nr b data/a.csv data/b.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a,b,c
|
||||
7,8,9
|
||||
4,5,6
|
||||
1,2,3
|
||||
</pre>
|
||||
|
||||
Likewise with ``mlr sort``, ``mlr tac``, and so on.
|
||||
Likewise with `mlr sort`, `mlr tac`, and so on.
|
||||
|
||||
## awk-like features: mlr filter and mlr put
|
||||
|
||||
* ``mlr filter`` includes/excludes records based on a filter expression, e.g. ``mlr filter '$count > 10'``.
|
||||
* `mlr filter` includes/excludes records based on a filter expression, e.g. `mlr filter '$count > 10'`.
|
||||
|
||||
* ``mlr put`` adds a new field as a function of others, e.g. ``mlr put '$xy = $x * $y'`` or ``mlr put '$counter = NR'``.
|
||||
* `mlr put` adds a new field as a function of others, e.g. `mlr put '$xy = $x * $y'` or `mlr put '$counter = NR'`.
|
||||
|
||||
* The ``$name`` syntax is straight from ``awk``'s ``$1 $2 $3`` (adapted to name-based indexing), as are the variables ``FS``, ``OFS``, ``RS``, ``ORS``, ``NF``, ``NR``, and ``FILENAME``. The ``ENV[...]`` syntax is from Ruby.
|
||||
* The `$name` syntax is straight from `awk`'s `$1 $2 $3` (adapted to name-based indexing), as are the variables `FS`, `OFS`, `RS`, `ORS`, `NF`, `NR`, and `FILENAME`. The `ENV[...]` syntax is from Ruby.
|
||||
|
||||
* While ``awk`` functions are record-based, Miller subcommands (or *verbs*) are stream-based: each of them maps a stream of records into another stream of records.
|
||||
* While `awk` functions are record-based, Miller subcommands (or *verbs*) are stream-based: each of them maps a stream of records into another stream of records.
|
||||
|
||||
* Like ``awk``, Miller (as of v5.0.0) allows you to define new functions within its ``put`` and ``filter`` expression language. Further programmability comes from chaining with ``then``.
|
||||
* Like `awk`, Miller (as of v5.0.0) allows you to define new functions within its `put` and `filter` expression language. Further programmability comes from chaining with `then`.
|
||||
|
||||
* As with ``awk``, ``$``-variables are stream variables and all verbs (such as ``cut``, ``stats1``, ``put``, etc.) as well as ``put``/``filter`` statements operate on streams. This means that you define actions to be done on each record and then stream your data through those actions. The built-in variables ``NF``, ``NR``, etc. change from one line to another, ``$x`` is a label for field ``x`` in the current record, and the input to ``sqrt($x)`` changes from one record to the next. The expression language for the ``put`` and ``filter`` verbs additionally allows you to define ``begin {...}`` and ``end {...}`` blocks for actions to be taken before and after records are processed, respectively.
|
||||
* As with `awk`, `$`-variables are stream variables and all verbs (such as `cut`, `stats1`, `put`, etc.) as well as `put`/`filter` statements operate on streams. This means that you define actions to be done on each record and then stream your data through those actions. The built-in variables `NF`, `NR`, etc. change from one line to another, `$x` is a label for field `x` in the current record, and the input to `sqrt($x)` changes from one record to the next. The expression language for the `put` and `filter` verbs additionally allows you to define `begin {...}` and `end {...}` blocks for actions to be taken before and after records are processed, respectively.
|
||||
|
||||
* As with ``awk``, Miller's ``put``/``filter`` language lets you set ``@sum=0`` before records are read, then update that sum on each record, then print its value at the end. Unlike ``awk``, Miller makes syntactically explicit the difference between variables with extent across all records (names starting with ``@``, such as ``@sum``) and variables which are local to the current expression (names starting without ``@``, such as ``sum``).
|
||||
* As with `awk`, Miller's `put`/`filter` language lets you set `@sum=0` before records are read, then update that sum on each record, then print its value at the end. Unlike `awk`, Miller makes syntactically explicit the difference between variables with extent across all records (names starting with `@`, such as `@sum`) and variables which are local to the current expression (names starting without `@`, such as `sum`).
|
||||
|
||||
* Miller can be faster than ``awk``, ``cut``, and so on, depending on platform; see also [Performance](performance.md). In particular, Miller's DSL syntax is parsed into Go control structures at startup time, with the bulk data-stream processing all done in Go.
|
||||
* Miller can be faster than `awk`, `cut`, and so on, depending on platform; see also [Performance](performance.md). In particular, Miller's DSL syntax is parsed into Go control structures at startup time, with the bulk data-stream processing all done in Go.
|
||||
|
||||
## See also
|
||||
|
||||
See [Verbs Reference](reference-verbs.md) for more on Miller's subcommands ``cat``, ``cut``, ``head``, ``sort``, ``tac``, ``tail``, ``top``, and ``uniq``, as well as [DSL reference](reference-dsl.md) for more on the awk-like ``mlr filter`` and ``mlr put``.
|
||||
See [Verbs Reference](reference-verbs.md) for more on Miller's subcommands `cat`, `cut`, `head`, `sort`, `tac`, `tail`, `top`, and `uniq`, as well as [DSL reference](reference-dsl.md) for more on the awk-like `mlr filter` and `mlr put`.
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
# Unix-toolkit context
|
||||
|
||||
How does Miller fit within the Unix toolkit (``grep``, ``sed``, ``awk``, etc.)?
|
||||
How does Miller fit within the Unix toolkit (`grep`, `sed`, `awk`, etc.)?
|
||||
|
||||
## File-format awareness
|
||||
|
||||
Miller respects CSV headers. If you do ``mlr --csv cat *.csv`` then the header line is written once:
|
||||
Miller respects CSV headers. If you do `mlr --csv cat *.csv` then the header line is written once:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
cat data/a.csv
|
||||
|
|
@ -22,26 +22,26 @@ GENMD_RUN_COMMAND
|
|||
mlr --csv sort -nr b data/a.csv data/b.csv
|
||||
GENMD_EOF
|
||||
|
||||
Likewise with ``mlr sort``, ``mlr tac``, and so on.
|
||||
Likewise with `mlr sort`, `mlr tac`, and so on.
|
||||
|
||||
## awk-like features: mlr filter and mlr put
|
||||
|
||||
* ``mlr filter`` includes/excludes records based on a filter expression, e.g. ``mlr filter '$count > 10'``.
|
||||
* `mlr filter` includes/excludes records based on a filter expression, e.g. `mlr filter '$count > 10'`.
|
||||
|
||||
* ``mlr put`` adds a new field as a function of others, e.g. ``mlr put '$xy = $x * $y'`` or ``mlr put '$counter = NR'``.
|
||||
* `mlr put` adds a new field as a function of others, e.g. `mlr put '$xy = $x * $y'` or `mlr put '$counter = NR'`.
|
||||
|
||||
* The ``$name`` syntax is straight from ``awk``'s ``$1 $2 $3`` (adapted to name-based indexing), as are the variables ``FS``, ``OFS``, ``RS``, ``ORS``, ``NF``, ``NR``, and ``FILENAME``. The ``ENV[...]`` syntax is from Ruby.
|
||||
* The `$name` syntax is straight from `awk`'s `$1 $2 $3` (adapted to name-based indexing), as are the variables `FS`, `OFS`, `RS`, `ORS`, `NF`, `NR`, and `FILENAME`. The `ENV[...]` syntax is from Ruby.
|
||||
|
||||
* While ``awk`` functions are record-based, Miller subcommands (or *verbs*) are stream-based: each of them maps a stream of records into another stream of records.
|
||||
* While `awk` functions are record-based, Miller subcommands (or *verbs*) are stream-based: each of them maps a stream of records into another stream of records.
|
||||
|
||||
* Like ``awk``, Miller (as of v5.0.0) allows you to define new functions within its ``put`` and ``filter`` expression language. Further programmability comes from chaining with ``then``.
|
||||
* Like `awk`, Miller (as of v5.0.0) allows you to define new functions within its `put` and `filter` expression language. Further programmability comes from chaining with `then`.
|
||||
|
||||
* As with ``awk``, ``$``-variables are stream variables and all verbs (such as ``cut``, ``stats1``, ``put``, etc.) as well as ``put``/``filter`` statements operate on streams. This means that you define actions to be done on each record and then stream your data through those actions. The built-in variables ``NF``, ``NR``, etc. change from one line to another, ``$x`` is a label for field ``x`` in the current record, and the input to ``sqrt($x)`` changes from one record to the next. The expression language for the ``put`` and ``filter`` verbs additionally allows you to define ``begin {...}`` and ``end {...}`` blocks for actions to be taken before and after records are processed, respectively.
|
||||
* As with `awk`, `$`-variables are stream variables and all verbs (such as `cut`, `stats1`, `put`, etc.) as well as `put`/`filter` statements operate on streams. This means that you define actions to be done on each record and then stream your data through those actions. The built-in variables `NF`, `NR`, etc. change from one line to another, `$x` is a label for field `x` in the current record, and the input to `sqrt($x)` changes from one record to the next. The expression language for the `put` and `filter` verbs additionally allows you to define `begin {...}` and `end {...}` blocks for actions to be taken before and after records are processed, respectively.
|
||||
|
||||
* As with ``awk``, Miller's ``put``/``filter`` language lets you set ``@sum=0`` before records are read, then update that sum on each record, then print its value at the end. Unlike ``awk``, Miller makes syntactically explicit the difference between variables with extent across all records (names starting with ``@``, such as ``@sum``) and variables which are local to the current expression (names starting without ``@``, such as ``sum``).
|
||||
* As with `awk`, Miller's `put`/`filter` language lets you set `@sum=0` before records are read, then update that sum on each record, then print its value at the end. Unlike `awk`, Miller makes syntactically explicit the difference between variables with extent across all records (names starting with `@`, such as `@sum`) and variables which are local to the current expression (names starting without `@`, such as `sum`).
|
||||
|
||||
* Miller can be faster than ``awk``, ``cut``, and so on, depending on platform; see also [Performance](performance.md). In particular, Miller's DSL syntax is parsed into Go control structures at startup time, with the bulk data-stream processing all done in Go.
|
||||
* Miller can be faster than `awk`, `cut`, and so on, depending on platform; see also [Performance](performance.md). In particular, Miller's DSL syntax is parsed into Go control structures at startup time, with the bulk data-stream processing all done in Go.
|
||||
|
||||
## See also
|
||||
|
||||
See [Verbs Reference](reference-verbs.md) for more on Miller's subcommands ``cat``, ``cut``, ``head``, ``sort``, ``tac``, ``tail``, ``top``, and ``uniq``, as well as [DSL reference](reference-dsl.md) for more on the awk-like ``mlr filter`` and ``mlr put``.
|
||||
See [Verbs Reference](reference-verbs.md) for more on Miller's subcommands `cat`, `cut`, `head`, `sort`, `tac`, `tail`, `top`, and `uniq`, as well as [DSL reference](reference-dsl.md) for more on the awk-like `mlr filter` and `mlr put`.
|
||||
|
|
|
|||
|
|
@ -19,11 +19,11 @@ including but not limited to the familiar CSV, TSV, and JSON. (Miller can handl
|
|||
|
||||
* Miller complements **data-analysis tools** such as **R**, **pandas**, etc.: you can use Miller to **clean** and **prepare** your data. While you can do **basic statistics** entirely in Miller, its streaming-data feature and single-pass algorithms enable you to **reduce very large data sets**.
|
||||
|
||||
* Miller complements SQL **databases**: you can slice, dice, and reformat data on the client side on its way into or out of a database. (Examples :ref:`here <sql-input-examples>` and :ref:`here <sql-output-examples>`.) You can also reap some of the benefits of databases for quick, setup-free one-off tasks when you just need to query some data in disk files in a hurry.
|
||||
* Miller complements SQL **databases**: you can slice, dice, and reformat data on the client side on its way into or out of a database. (See [SQL Examples](sql-examples.md).) You can also reap some of the benefits of databases for quick, setup-free one-off tasks when you just need to query some data in disk files in a hurry.
|
||||
|
||||
* Miller also goes beyond the classic Unix tools by stepping fully into our modern, **no-SQL** world: its essential record-heterogeneity property allows Miller to operate on data where records with different schema (field names) are interleaved.
|
||||
|
||||
* Miller is **streaming**: most operations need only a single record in memory at a time, rather than ingesting all input before producing any output. For those operations which require deeper retention (``sort``, ``tac``, ``stats1``), Miller retains only as much data as needed. This means that whenever functionally possible, you can operate on files which are larger than your system's available RAM, and you can use Miller in **tail -f** contexts.
|
||||
* Miller is **streaming**: most operations need only a single record in memory at a time, rather than ingesting all input before producing any output. For those operations which require deeper retention (`sort`, `tac`, `stats1`), Miller retains only as much data as needed. This means that whenever functionally possible, you can operate on files which are larger than your system's available RAM, and you can use Miller in **tail -f** contexts.
|
||||
|
||||
* Miller is **pipe-friendly** and interoperates with the Unix toolkit
|
||||
|
||||
|
|
@ -31,10 +31,10 @@ including but not limited to the familiar CSV, TSV, and JSON. (Miller can handl
|
|||
|
||||
* Miller does **conversion** between formats
|
||||
|
||||
* Miller's **processing is format-aware**: e.g. CSV ``sort`` and ``tac`` keep header lines first
|
||||
* Miller's **processing is format-aware**: e.g. CSV `sort` and `tac` keep header lines first
|
||||
|
||||
* Miller has high-throughput **performance** on par with the Unix toolkit
|
||||
|
||||
* Not unlike [jq](https://stedolan.github.io/jq/) (for JSON), Miller is written in Go which is a portable, modern language, and Miller has no runtime dependencies. You can download or compile a single binary, ``scp`` it to a faraway machine, and expect it to work.
|
||||
* Not unlike [jq](https://stedolan.github.io/jq/) (for JSON), Miller is written in Go which is a portable, modern language, and Miller has no runtime dependencies. You can download or compile a single binary, `scp` it to a faraway machine, and expect it to work.
|
||||
|
||||
Releases and release notes: https://github.com/johnkerl/miller/releases.
|
||||
Releases and release notes: [https://github.com/johnkerl/miller/releases](https://github.com/johnkerl/miller/releases).
|
||||
|
|
|
|||
|
|
@ -18,11 +18,11 @@ including but not limited to the familiar CSV, TSV, and JSON. (Miller can handl
|
|||
|
||||
* Miller complements **data-analysis tools** such as **R**, **pandas**, etc.: you can use Miller to **clean** and **prepare** your data. While you can do **basic statistics** entirely in Miller, its streaming-data feature and single-pass algorithms enable you to **reduce very large data sets**.
|
||||
|
||||
* Miller complements SQL **databases**: you can slice, dice, and reformat data on the client side on its way into or out of a database. (Examples :ref:`here <sql-input-examples>` and :ref:`here <sql-output-examples>`.) You can also reap some of the benefits of databases for quick, setup-free one-off tasks when you just need to query some data in disk files in a hurry.
|
||||
* Miller complements SQL **databases**: you can slice, dice, and reformat data on the client side on its way into or out of a database. (See [SQL Examples](sql-examples.md).) You can also reap some of the benefits of databases for quick, setup-free one-off tasks when you just need to query some data in disk files in a hurry.
|
||||
|
||||
* Miller also goes beyond the classic Unix tools by stepping fully into our modern, **no-SQL** world: its essential record-heterogeneity property allows Miller to operate on data where records with different schema (field names) are interleaved.
|
||||
|
||||
* Miller is **streaming**: most operations need only a single record in memory at a time, rather than ingesting all input before producing any output. For those operations which require deeper retention (``sort``, ``tac``, ``stats1``), Miller retains only as much data as needed. This means that whenever functionally possible, you can operate on files which are larger than your system's available RAM, and you can use Miller in **tail -f** contexts.
|
||||
* Miller is **streaming**: most operations need only a single record in memory at a time, rather than ingesting all input before producing any output. For those operations which require deeper retention (`sort`, `tac`, `stats1`), Miller retains only as much data as needed. This means that whenever functionally possible, you can operate on files which are larger than your system's available RAM, and you can use Miller in **tail -f** contexts.
|
||||
|
||||
* Miller is **pipe-friendly** and interoperates with the Unix toolkit
|
||||
|
||||
|
|
@ -30,10 +30,10 @@ including but not limited to the familiar CSV, TSV, and JSON. (Miller can handl
|
|||
|
||||
* Miller does **conversion** between formats
|
||||
|
||||
* Miller's **processing is format-aware**: e.g. CSV ``sort`` and ``tac`` keep header lines first
|
||||
* Miller's **processing is format-aware**: e.g. CSV `sort` and `tac` keep header lines first
|
||||
|
||||
* Miller has high-throughput **performance** on par with the Unix toolkit
|
||||
|
||||
* Not unlike [jq](https://stedolan.github.io/jq/) (for JSON), Miller is written in Go which is a portable, modern language, and Miller has no runtime dependencies. You can download or compile a single binary, ``scp`` it to a faraway machine, and expect it to work.
|
||||
* Not unlike [jq](https://stedolan.github.io/jq/) (for JSON), Miller is written in Go which is a portable, modern language, and Miller has no runtime dependencies. You can download or compile a single binary, `scp` it to a faraway machine, and expect it to work.
|
||||
|
||||
Releases and release notes: https://github.com/johnkerl/miller/releases.
|
||||
Releases and release notes: [https://github.com/johnkerl/miller/releases](https://github.com/johnkerl/miller/releases).
|
||||
|
|
|
|||
|
|
@ -7,8 +7,10 @@ Additionally, Miller gives you the option of including comments within your data
|
|||
|
||||
## Examples
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr help data-formats</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
CSV/CSV-lite: comma-separated values with separate header line
|
||||
TSV: same but with tabs in places of commas
|
||||
+---------------------+
|
||||
|
|
@ -71,28 +73,26 @@ NIDX: implicitly numerically indexed (Unix-toolkit style)
|
|||
+---------------------+
|
||||
</pre>
|
||||
|
||||
.. _file-formats-csv:
|
||||
|
||||
## CSV/TSV/ASV/USV/etc.
|
||||
|
||||
When ``mlr`` is invoked with the ``--csv`` or ``--csvlite`` option, key names are found on the first record and values are taken from subsequent records. This includes the case of CSV-formatted files. See [Record Heterogeneity](record-heterogeneity.md) for how Miller handles changes of field names within a single data stream.
|
||||
When `mlr` is invoked with the `--csv` or `--csvlite` option, key names are found on the first record and values are taken from subsequent records. This includes the case of CSV-formatted files. See [Record Heterogeneity](record-heterogeneity.md) for how Miller handles changes of field names within a single data stream.
|
||||
|
||||
Miller has record separator ``RS`` and field separator ``FS``, just as ``awk`` does. For TSV, use ``--fs tab``; to convert TSV to CSV, use ``--ifs tab --ofs comma``, etc. (See also :ref:`reference-separators`.)
|
||||
Miller has record separator `RS` and field separator `FS`, just as `awk` does. For TSV, use `--fs tab`; to convert TSV to CSV, use `--ifs tab --ofs comma`, etc. (See also [Record/field/pair separators](reference-main-io-options.md#recordfieldpair-separators).)
|
||||
|
||||
**TSV (tab-separated values):** the following are synonymous pairs:
|
||||
|
||||
* ``--tsv`` and ``--csv --fs tab``
|
||||
* ``--itsv`` and ``--icsv --ifs tab``
|
||||
* ``--otsv`` and ``--ocsv --ofs tab``
|
||||
* ``--tsvlite`` and ``--csvlite --fs tab``
|
||||
* ``--itsvlite`` and ``--icsvlite --ifs tab``
|
||||
* ``--otsvlite`` and ``--ocsvlite --ofs tab``
|
||||
* `--tsv` and `--csv --fs tab`
|
||||
* `--itsv` and `--icsv --ifs tab`
|
||||
* `--otsv` and `--ocsv --ofs tab`
|
||||
* `--tsvlite` and `--csvlite --fs tab`
|
||||
* `--itsvlite` and `--icsvlite --ifs tab`
|
||||
* `--otsvlite` and `--ocsvlite --ofs tab`
|
||||
|
||||
**ASV (ASCII-separated values):** the flags ``--asv``, ``--iasv``, ``--oasv``, ``--asvlite``, ``--iasvlite``, and ``--oasvlite`` are analogous except they use ASCII FS and RS 0x1f and 0x1e, respectively.
|
||||
**ASV (ASCII-separated values):** the flags `--asv`, `--iasv`, `--oasv`, `--asvlite`, `--iasvlite`, and `--oasvlite` are analogous except they use ASCII FS and RS 0x1f and 0x1e, respectively.
|
||||
|
||||
**USV (Unicode-separated values):** likewise, the flags ``--usv``, ``--iusv``, ``--ousv``, ``--usvlite``, ``--iusvlite``, and ``--ousvlite`` use Unicode FS and RS U+241F (UTF-8 0x0xe2909f) and U+241E (UTF-8 0xe2909e), respectively.
|
||||
**USV (Unicode-separated values):** likewise, the flags `--usv`, `--iusv`, `--ousv`, `--usvlite`, `--iusvlite`, and `--ousvlite` use Unicode FS and RS U+241F (UTF-8 0x0xe2909f) and U+241E (UTF-8 0xe2909e), respectively.
|
||||
|
||||
Miller's ``--csv`` flag supports [RFC-4180 CSV](https://tools.ietf.org/html/rfc4180). This includes CRLF line-terminators by default, regardless of platform.
|
||||
Miller's `--csv` flag supports [RFC-4180 CSV](https://tools.ietf.org/html/rfc4180). This includes CRLF line-terminators by default, regardless of platform.
|
||||
|
||||
Here are the differences between CSV and CSV-lite:
|
||||
|
||||
|
|
@ -106,16 +106,16 @@ Here are things they have in common:
|
|||
|
||||
* The ability to specify record/field separators other than the default, e.g. CR-LF vs. LF, or tab instead of comma for TSV, and so on.
|
||||
|
||||
* The ``--implicit-csv-header`` flag for input and the ``--headerless-csv-output`` flag for output.
|
||||
|
||||
.. _file-formats-dkvp:
|
||||
* The `--implicit-csv-header` flag for input and the `--headerless-csv-output` flag for output.
|
||||
|
||||
## DKVP: Key-value pairs
|
||||
|
||||
Miller's default file format is DKVP, for **delimited key-value pairs**. Example:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr cat data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,b=pan,i=1,x=0.3467901443380824,y=0.7268028627434533
|
||||
a=eks,b=pan,i=2,x=0.7586799647899636,y=0.5221511083334797
|
||||
a=wye,b=wye,i=3,x=0.20460330576630303,y=0.33831852551664776
|
||||
|
|
@ -125,51 +125,51 @@ a=wye,b=pan,i=5,x=0.5732889198020006,y=0.8636244699032729
|
|||
|
||||
Such data are easy to generate, e.g. in Ruby with
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
puts "host=#{hostname},seconds=#{t2-t1},message=#{msg}"
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
puts mymap.collect{|k,v| "#{k}=#{v}"}.join(',')
|
||||
</pre>
|
||||
|
||||
or ``print`` statements in various languages, e.g.
|
||||
or `print` statements in various languages, e.g.
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
echo "type=3,user=$USER,date=$date\n";
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
logger.log("type=3,user=$USER,date=$date\n");
|
||||
</pre>
|
||||
|
||||
Fields lacking an IPS will have positional index (starting at 1) used as the key, as in NIDX format. For example, ``dish=7,egg=8,flint`` is parsed as ``"dish" => "7", "egg" => "8", "3" => "flint"`` and ``dish,egg,flint`` is parsed as ``"1" => "dish", "2" => "egg", "3" => "flint"``.
|
||||
Fields lacking an IPS will have positional index (starting at 1) used as the key, as in NIDX format. For example, `dish=7,egg=8,flint` is parsed as `"dish" => "7", "egg" => "8", "3" => "flint"` and `dish,egg,flint` is parsed as `"1" => "dish", "2" => "egg", "3" => "flint"`.
|
||||
|
||||
As discussed in [Record Heterogeneity](record-heterogeneity.md), Miller handles changes of field names within the same data stream. But using DKVP format this is particularly natural. One of my favorite use-cases for Miller is in application/server logs, where I log all sorts of lines such as
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
resource=/path/to/file,loadsec=0.45,ok=true
|
||||
record_count=100, resource=/path/to/file
|
||||
resource=/some/other/path,loadsec=0.97,ok=false
|
||||
</pre>
|
||||
|
||||
etc. and I just log them as needed. Then later, I can use ``grep``, ``mlr --opprint group-like``, etc.
|
||||
etc. and I just log them as needed. Then later, I can use `grep`, `mlr --opprint group-like`, etc.
|
||||
to analyze my logs.
|
||||
|
||||
See :doc:`reference-main-io-options` regarding how to specify separators other than the default equals-sign and comma.
|
||||
|
||||
.. _file-formats-nidx:
|
||||
See [Reference: I/O options](reference-main-io-options.md) regarding how to specify separators other than the default equals-sign and comma.
|
||||
|
||||
## NIDX: Index-numbered (toolkit style)
|
||||
|
||||
With ``--inidx --ifs ' ' --repifs``, Miller splits lines on whitespace and assigns integer field names starting with 1.
|
||||
With `--inidx --ifs ' ' --repifs`, Miller splits lines on whitespace and assigns integer field names starting with 1.
|
||||
|
||||
This recapitulates Unix-toolkit behavior.
|
||||
|
||||
Example with index-numbered output:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,b=pan,i=1,x=0.3467901443380824,y=0.7268028627434533
|
||||
a=eks,b=pan,i=2,x=0.7586799647899636,y=0.5221511083334797
|
||||
a=wye,b=wye,i=3,x=0.20460330576630303,y=0.33831852551664776
|
||||
|
|
@ -177,8 +177,10 @@ a=eks,b=wye,i=4,x=0.38139939387114097,y=0.13418874328430463
|
|||
a=wye,b=pan,i=5,x=0.5732889198020006,y=0.8636244699032729
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --onidx --ofs ' ' cat data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
pan pan 1 0.3467901443380824 0.7268028627434533
|
||||
eks pan 2 0.7586799647899636 0.5221511083334797
|
||||
wye wye 3 0.20460330576630303 0.33831852551664776
|
||||
|
|
@ -188,15 +190,19 @@ wye pan 5 0.5732889198020006 0.8636244699032729
|
|||
|
||||
Example with index-numbered input:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/mydata.txt</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
oh say can you see
|
||||
by the dawn's
|
||||
early light
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --inidx --ifs ' ' --odkvp cat data/mydata.txt</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
1=oh,2=say,3=can,4=you,5=see
|
||||
1=by,2=the,3=dawn's
|
||||
1=early,2=light
|
||||
|
|
@ -204,23 +210,25 @@ early light
|
|||
|
||||
Example with index-numbered input and output:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/mydata.txt</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
oh say can you see
|
||||
by the dawn's
|
||||
early light
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --nidx --fs ' ' --repifs cut -f 2,3 data/mydata.txt</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
say can
|
||||
the dawn's
|
||||
light
|
||||
</pre>
|
||||
|
||||
.. _file-formats-json:
|
||||
|
||||
## Tabular JSON
|
||||
## JSON
|
||||
|
||||
JSON is a format which supports arbitrarily deep nesting of "objects" (hashmaps) and "arrays" (lists), while Miller is a tool for handling **tabular data** only. This means Miller cannot (and should not) handle arbitrary JSON. (Check out [jq](https://stedolan.github.io/jq/).)
|
||||
|
||||
|
|
@ -232,8 +240,10 @@ By *tabular JSON* I mean the data is either a sequence of one or more objects, o
|
|||
|
||||
An **array of single-level objects** is, quite simply, **a table**:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --json head -n 2 then cut -f color,shape data/json-example-1.json</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
{
|
||||
"color": "yellow",
|
||||
"shape": "triangle"
|
||||
|
|
@ -244,8 +254,10 @@ An **array of single-level objects** is, quite simply, **a table**:
|
|||
}
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --json --jvstack head -n 2 then cut -f color,u,v data/json-example-1.json</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
{
|
||||
"color": "yellow",
|
||||
"u": 0.6321695890307647,
|
||||
|
|
@ -258,8 +270,10 @@ An **array of single-level objects** is, quite simply, **a table**:
|
|||
}
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --ijson --opprint stats1 -a mean,stddev,count -f u -g shape data/json-example-1.json</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
shape u_mean u_stddev u_count
|
||||
triangle 0.5839952367477192 0.13118354465618046 3
|
||||
square 0.409355036804889 0.3654281755508655 4
|
||||
|
|
@ -270,8 +284,10 @@ circle 0.36601268553826866 0.2090944565900053 3
|
|||
|
||||
Additionally, Miller can **tabularize nested objects by concatentating keys**:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --json --jvstack head -n 2 data/json-example-2.json</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
{
|
||||
"flag": 1,
|
||||
"i": 11,
|
||||
|
|
@ -302,8 +318,10 @@ Additionally, Miller can **tabularize nested objects by concatentating keys**:
|
|||
}
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --ijson --opprint head -n 4 data/json-example-2.json</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
flag i attributes.color attributes.shape values.u values.v values.w values.x
|
||||
1 11 yellow triangle 0.632170 0.988721 0.436498 5.798188
|
||||
1 15 red square 0.219668 0.001257 0.792778 2.944117
|
||||
|
|
@ -311,12 +329,14 @@ flag i attributes.color attributes.shape values.u values.v values.w values.x
|
|||
0 48 red square 0.956274 0.746720 0.775542 7.117831
|
||||
</pre>
|
||||
|
||||
Note in particular that as far as Miller's ``put`` and ``filter``, as well as other I/O formats, are concerned, these are simply field names with colons in them:
|
||||
Note in particular that as far as Miller's `put` and `filter`, as well as other I/O formats, are concerned, these are simply field names with colons in them:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --json --jvstack head -n 1 \</b>
|
||||
<b> then put '${values:uv} = ${values:u} * ${values:v}' \</b>
|
||||
<b> data/json-example-2.json</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
{
|
||||
"flag": 1,
|
||||
"i": 11,
|
||||
|
|
@ -335,12 +355,14 @@ Note in particular that as far as Miller's ``put`` and ``filter``, as well as ot
|
|||
|
||||
### Arrays
|
||||
|
||||
Arrays (TODO: update for Miller6) aren't supported in Miller's ``put``/``filter`` DSL. By default, JSON arrays are read in as integer-keyed maps.
|
||||
Arrays (TODO: update for Miller6) aren't supported in Miller's `put`/`filter` DSL. By default, JSON arrays are read in as integer-keyed maps.
|
||||
|
||||
Suppose we have arrays like this in our input data:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/json-example-3.json</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
{
|
||||
"label": "orange",
|
||||
"values": [12.2, 13.8, 17.2]
|
||||
|
|
@ -353,8 +375,10 @@ Suppose we have arrays like this in our input data:
|
|||
|
||||
Then integer indices (starting from 0 and counting up) are used as map keys:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --ijson --oxtab cat data/json-example-3.json</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
label orange
|
||||
values.1 12.2
|
||||
values.2 13.8
|
||||
|
|
@ -367,8 +391,10 @@ values.2 32.4
|
|||
|
||||
When the data are written back out as JSON, field names are re-expanded as above, but what were arrays on input are now maps on output:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --json --jvstack cat data/json-example-3.json</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
{
|
||||
"label": "orange",
|
||||
"values": [12.2, 13.8, 17.2]
|
||||
|
|
@ -381,34 +407,34 @@ When the data are written back out as JSON, field names are re-expanded as above
|
|||
|
||||
This is non-ideal, but it allows Miller (5.x release being latest as of this writing) to handle JSON arrays at all.
|
||||
|
||||
You might also use ``mlr --json-skip-arrays-on-input`` or ``mlr --json-fatal-arrays-on-input``.
|
||||
You might also use `mlr --json-skip-arrays-on-input` or `mlr --json-fatal-arrays-on-input`.
|
||||
|
||||
To truly handle JSON, please use a JSON-processing tool such as [jq](https://stedolan.github.io/jq/).
|
||||
|
||||
### Formatting JSON options
|
||||
|
||||
JSON isn't a parameterized format, so ``RS``, ``FS``, ``PS`` aren't specifiable. Nonetheless, you can do the following:
|
||||
JSON isn't a parameterized format, so `RS`, `FS`, `PS` aren't specifiable. Nonetheless, you can do the following:
|
||||
|
||||
* Use ``--jvstack`` to pretty-print JSON objects with multi-line (vertically stacked) spacing. By default, each Miller record (JSON object) is one per line.
|
||||
* Use `--jvstack` to pretty-print JSON objects with multi-line (vertically stacked) spacing. By default, each Miller record (JSON object) is one per line.
|
||||
|
||||
* Keystroke-savers: ``--jsonx`` simply means ``--json --jvstack``, and ``--ojsonx`` simply means ``--ojson --jvstack``.
|
||||
* Keystroke-savers: `--jsonx` simply means `--json --jvstack`, and `--ojsonx` simply means `--ojson --jvstack`.
|
||||
|
||||
* Use ``--jlistwrap`` to print the sequence of JSON objects wrapped in an outermost ``[`` and ``]``. By default, these aren't printed.
|
||||
* Use `--jlistwrap` to print the sequence of JSON objects wrapped in an outermost `[` and `]`. By default, these aren't printed.
|
||||
|
||||
* Use ``--jquoteall`` to double-quote all object values. By default, integers, floating-point numbers, and booleans ``true`` and ``false`` are not double-quoted when they appear as JSON-object keys.
|
||||
* Use `--jquoteall` to double-quote all object values. By default, integers, floating-point numbers, and booleans `true` and `false` are not double-quoted when they appear as JSON-object keys.
|
||||
|
||||
* Use ``--jflatsep youmd.inghere`` to specify the string used for key concatenation: this defaults to a single colon.
|
||||
* Use `--jflatsep youmd.inghere` to specify the string used for key concatenation: this defaults to a single colon.
|
||||
|
||||
Again, please see [jq](https://stedolan.github.io/jq/) for a truly powerful, JSON-specific tool.
|
||||
|
||||
.. _file-formats-pprint:
|
||||
|
||||
## PPRINT: Pretty-printed tabular
|
||||
|
||||
Miller's pretty-print format is like CSV, but column-aligned. For example, compare
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --ocsv cat data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a,b,i,x,y
|
||||
pan,pan,1,0.3467901443380824,0.7268028627434533
|
||||
eks,pan,2,0.7586799647899636,0.5221511083334797
|
||||
|
|
@ -417,8 +443,10 @@ eks,wye,4,0.38139939387114097,0.13418874328430463
|
|||
wye,pan,5,0.5732889198020006,0.8636244699032729
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --opprint cat data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a b i x y
|
||||
pan pan 1 0.3467901443380824 0.7268028627434533
|
||||
eks pan 2 0.7586799647899636 0.5221511083334797
|
||||
|
|
@ -427,14 +455,16 @@ eks wye 4 0.38139939387114097 0.13418874328430463
|
|||
wye pan 5 0.5732889198020006 0.8636244699032729
|
||||
</pre>
|
||||
|
||||
Note that while Miller is a line-at-a-time processor and retains input lines in memory only where necessary (e.g. for sort), pretty-print output requires it to accumulate all input lines (so that it can compute maximum column widths) before producing any output. This has two consequences: (a) pretty-print output won't work on ``tail -f`` contexts, where Miller will be waiting for an end-of-file marker which never arrives; (b) pretty-print output for large files is constrained by available machine memory.
|
||||
Note that while Miller is a line-at-a-time processor and retains input lines in memory only where necessary (e.g. for sort), pretty-print output requires it to accumulate all input lines (so that it can compute maximum column widths) before producing any output. This has two consequences: (a) pretty-print output won't work on `tail -f` contexts, where Miller will be waiting for an end-of-file marker which never arrives; (b) pretty-print output for large files is constrained by available machine memory.
|
||||
|
||||
See [Record Heterogeneity](record-heterogeneity.md) for how Miller handles changes of field names within a single data stream.
|
||||
|
||||
For output only (this isn't supported in the input-scanner as of 5.0.0) you can use ``--barred`` with pprint output format:
|
||||
For output only (this isn't supported in the input-scanner as of 5.0.0) you can use `--barred` with pprint output format:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --opprint --barred cat data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
+-----+-----+---+---------------------+---------------------+
|
||||
| a | b | i | x | y |
|
||||
+-----+-----+---+---------------------+---------------------+
|
||||
|
|
@ -446,14 +476,12 @@ For output only (this isn't supported in the input-scanner as of 5.0.0) you can
|
|||
+-----+-----+---+---------------------+---------------------+
|
||||
</pre>
|
||||
|
||||
.. _file-formats-xtab:
|
||||
|
||||
## XTAB: Vertical tabular
|
||||
|
||||
This is perhaps most useful for looking a very wide and/or multi-column data which causes line-wraps on the screen (but see also
|
||||
[ngrid](https://github.com/twosigma/ngrid/) for an entirely different, very powerful option). Namely:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
$ grep -v '^#' /etc/passwd | head -n 6 | mlr --nidx --fs : --opprint cat
|
||||
1 2 3 4 5 6 7
|
||||
nobody * -2 -2 Unprivileged User /var/empty /usr/bin/false
|
||||
|
|
@ -464,7 +492,7 @@ _taskgated * 13 13 Task Gate Daemon /var/empty /usr/bin/false
|
|||
_networkd * 24 24 Network Services /var/networkd /usr/bin/false
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
$ grep -v '^#' /etc/passwd | head -n 2 | mlr --nidx --fs : --oxtab cat
|
||||
1 nobody
|
||||
2 *
|
||||
|
|
@ -483,7 +511,7 @@ $ grep -v '^#' /etc/passwd | head -n 2 | mlr --nidx --fs : --oxtab cat
|
|||
7 /bin/sh
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
$ grep -v '^#' /etc/passwd | head -n 2 | \
|
||||
mlr --nidx --fs : --ojson --jvstack --jlistwrap label name,password,uid,gid,gecos,home_dir,shell
|
||||
[
|
||||
|
|
@ -512,8 +540,10 @@ $ grep -v '^#' /etc/passwd | head -n 2 | \
|
|||
|
||||
Markdown format looks like this:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --omd cat data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
| a | b | i | x | y |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| pan | pan | 1 | 0.3467901443380824 | 0.7268028627434533 |
|
||||
|
|
@ -531,10 +561,12 @@ As of Miller 4.3.0, markdown format is supported only for output, not input.
|
|||
|
||||
## Data-conversion keystroke-savers
|
||||
|
||||
While you can do format conversion using ``mlr --icsv --ojson cat myfile.csv``, there are also keystroke-savers for this purpose, such as ``mlr --c2j cat myfile.csv``. For a complete list:
|
||||
While you can do format conversion using `mlr --icsv --ojson cat myfile.csv`, there are also keystroke-savers for this purpose, such as `mlr --c2j cat myfile.csv`. For a complete list:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr help format-conversion</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
As keystroke-savers for format-conversion you may use the following:
|
||||
--c2t --c2d --c2n --c2j --c2x --c2p --c2m
|
||||
--t2c --t2d --t2n --t2j --t2x --t2p --t2m
|
||||
|
|
@ -550,24 +582,26 @@ output only.
|
|||
|
||||
## Autodetect of line endings
|
||||
|
||||
Default line endings (``--irs`` and ``--ors``) are ``'auto'`` which means **autodetect from the input file format**, as long as the input file(s) have lines ending in either LF (also known as linefeed, ``'\n'``, ``0x0a``, Unix-style) or CRLF (also known as carriage-return/linefeed pairs, ``'\r\n'``, ``0x0d 0x0a``, Windows style).
|
||||
Default line endings (`--irs` and `--ors`) are `'auto'` which means **autodetect from the input file format**, as long as the input file(s) have lines ending in either LF (also known as linefeed, `'\n'`, `0x0a`, Unix-style) or CRLF (also known as carriage-return/linefeed pairs, `'\r\n'`, `0x0d 0x0a`, Windows style).
|
||||
|
||||
**If both IRS and ORS are auto (which is the default) then LF input will lead to LF output and CRLF input will lead to CRLF output, regardless of the platform you're running on.**
|
||||
|
||||
The line-ending autodetector triggers on the first line ending detected in the input stream. E.g. if you specify a CRLF-terminated file on the command line followed by an LF-terminated file then autodetected line endings will be CRLF.
|
||||
|
||||
If you use ``--ors {something else}`` with (default or explicitly specified) ``--irs auto`` then line endings are autodetected on input and set to what you specify on output.
|
||||
If you use `--ors {something else}` with (default or explicitly specified) `--irs auto` then line endings are autodetected on input and set to what you specify on output.
|
||||
|
||||
If you use ``--irs {something else}`` with (default or explicitly specified) ``--ors auto`` then the output line endings used are LF on Unix/Linux/BSD/MacOSX, and CRLF on Windows.
|
||||
If you use `--irs {something else}` with (default or explicitly specified) `--ors auto` then the output line endings used are LF on Unix/Linux/BSD/MacOSX, and CRLF on Windows.
|
||||
|
||||
See also :ref:`reference-separators` for more information about record/field/pair separators.
|
||||
See also [Record/field/pair separators](reference-main-io-options.md#recordfieldpair-separators) for more information about record/field/pair separators.
|
||||
|
||||
## Comments in data
|
||||
|
||||
You can include comments within your data files, and either have them ignored, or passed directly through to the standard output as soon as they are encountered:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr help comments-in-data</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
--skip-comments Ignore commented lines (prefixed by "#")
|
||||
within the input.
|
||||
--skip-comments-with {string} Ignore commented lines within input, with
|
||||
|
|
@ -589,8 +623,10 @@ Notes:
|
|||
|
||||
Examples:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/budget.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
# Asana -- here are the budget figures you asked for!
|
||||
type,quantity
|
||||
purple,456.78
|
||||
|
|
@ -598,16 +634,20 @@ green,678.12
|
|||
orange,123.45
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --skip-comments --icsv --opprint sort -nr quantity data/budget.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
type quantity
|
||||
green 678.12
|
||||
purple 456.78
|
||||
orange 123.45
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --pass-comments --icsv --opprint sort -nr quantity data/budget.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
# Asana -- here are the budget figures you asked for!
|
||||
type quantity
|
||||
green 678.12
|
||||
|
|
|
|||
|
|
@ -10,28 +10,26 @@ GENMD_RUN_COMMAND
|
|||
mlr help data-formats
|
||||
GENMD_EOF
|
||||
|
||||
.. _file-formats-csv:
|
||||
|
||||
## CSV/TSV/ASV/USV/etc.
|
||||
|
||||
When ``mlr`` is invoked with the ``--csv`` or ``--csvlite`` option, key names are found on the first record and values are taken from subsequent records. This includes the case of CSV-formatted files. See [Record Heterogeneity](record-heterogeneity.md) for how Miller handles changes of field names within a single data stream.
|
||||
When `mlr` is invoked with the `--csv` or `--csvlite` option, key names are found on the first record and values are taken from subsequent records. This includes the case of CSV-formatted files. See [Record Heterogeneity](record-heterogeneity.md) for how Miller handles changes of field names within a single data stream.
|
||||
|
||||
Miller has record separator ``RS`` and field separator ``FS``, just as ``awk`` does. For TSV, use ``--fs tab``; to convert TSV to CSV, use ``--ifs tab --ofs comma``, etc. (See also :ref:`reference-separators`.)
|
||||
Miller has record separator `RS` and field separator `FS`, just as `awk` does. For TSV, use `--fs tab`; to convert TSV to CSV, use `--ifs tab --ofs comma`, etc. (See also [Record/field/pair separators](reference-main-io-options.md#recordfieldpair-separators).)
|
||||
|
||||
**TSV (tab-separated values):** the following are synonymous pairs:
|
||||
|
||||
* ``--tsv`` and ``--csv --fs tab``
|
||||
* ``--itsv`` and ``--icsv --ifs tab``
|
||||
* ``--otsv`` and ``--ocsv --ofs tab``
|
||||
* ``--tsvlite`` and ``--csvlite --fs tab``
|
||||
* ``--itsvlite`` and ``--icsvlite --ifs tab``
|
||||
* ``--otsvlite`` and ``--ocsvlite --ofs tab``
|
||||
* `--tsv` and `--csv --fs tab`
|
||||
* `--itsv` and `--icsv --ifs tab`
|
||||
* `--otsv` and `--ocsv --ofs tab`
|
||||
* `--tsvlite` and `--csvlite --fs tab`
|
||||
* `--itsvlite` and `--icsvlite --ifs tab`
|
||||
* `--otsvlite` and `--ocsvlite --ofs tab`
|
||||
|
||||
**ASV (ASCII-separated values):** the flags ``--asv``, ``--iasv``, ``--oasv``, ``--asvlite``, ``--iasvlite``, and ``--oasvlite`` are analogous except they use ASCII FS and RS 0x1f and 0x1e, respectively.
|
||||
**ASV (ASCII-separated values):** the flags `--asv`, `--iasv`, `--oasv`, `--asvlite`, `--iasvlite`, and `--oasvlite` are analogous except they use ASCII FS and RS 0x1f and 0x1e, respectively.
|
||||
|
||||
**USV (Unicode-separated values):** likewise, the flags ``--usv``, ``--iusv``, ``--ousv``, ``--usvlite``, ``--iusvlite``, and ``--ousvlite`` use Unicode FS and RS U+241F (UTF-8 0x0xe2909f) and U+241E (UTF-8 0xe2909e), respectively.
|
||||
**USV (Unicode-separated values):** likewise, the flags `--usv`, `--iusv`, `--ousv`, `--usvlite`, `--iusvlite`, and `--ousvlite` use Unicode FS and RS U+241F (UTF-8 0x0xe2909f) and U+241E (UTF-8 0xe2909e), respectively.
|
||||
|
||||
Miller's ``--csv`` flag supports [RFC-4180 CSV](https://tools.ietf.org/html/rfc4180). This includes CRLF line-terminators by default, regardless of platform.
|
||||
Miller's `--csv` flag supports [RFC-4180 CSV](https://tools.ietf.org/html/rfc4180). This includes CRLF line-terminators by default, regardless of platform.
|
||||
|
||||
Here are the differences between CSV and CSV-lite:
|
||||
|
||||
|
|
@ -45,9 +43,7 @@ Here are things they have in common:
|
|||
|
||||
* The ability to specify record/field separators other than the default, e.g. CR-LF vs. LF, or tab instead of comma for TSV, and so on.
|
||||
|
||||
* The ``--implicit-csv-header`` flag for input and the ``--headerless-csv-output`` flag for output.
|
||||
|
||||
.. _file-formats-dkvp:
|
||||
* The `--implicit-csv-header` flag for input and the `--headerless-csv-output` flag for output.
|
||||
|
||||
## DKVP: Key-value pairs
|
||||
|
||||
|
|
@ -67,7 +63,7 @@ GENMD_CARDIFY
|
|||
puts mymap.collect{|k,v| "#{k}=#{v}"}.join(',')
|
||||
GENMD_EOF
|
||||
|
||||
or ``print`` statements in various languages, e.g.
|
||||
or `print` statements in various languages, e.g.
|
||||
|
||||
GENMD_CARDIFY
|
||||
echo "type=3,user=$USER,date=$date\n";
|
||||
|
|
@ -77,7 +73,7 @@ GENMD_CARDIFY
|
|||
logger.log("type=3,user=$USER,date=$date\n");
|
||||
GENMD_EOF
|
||||
|
||||
Fields lacking an IPS will have positional index (starting at 1) used as the key, as in NIDX format. For example, ``dish=7,egg=8,flint`` is parsed as ``"dish" => "7", "egg" => "8", "3" => "flint"`` and ``dish,egg,flint`` is parsed as ``"1" => "dish", "2" => "egg", "3" => "flint"``.
|
||||
Fields lacking an IPS will have positional index (starting at 1) used as the key, as in NIDX format. For example, `dish=7,egg=8,flint` is parsed as `"dish" => "7", "egg" => "8", "3" => "flint"` and `dish,egg,flint` is parsed as `"1" => "dish", "2" => "egg", "3" => "flint"`.
|
||||
|
||||
As discussed in [Record Heterogeneity](record-heterogeneity.md), Miller handles changes of field names within the same data stream. But using DKVP format this is particularly natural. One of my favorite use-cases for Miller is in application/server logs, where I log all sorts of lines such as
|
||||
|
||||
|
|
@ -87,16 +83,14 @@ record_count=100, resource=/path/to/file
|
|||
resource=/some/other/path,loadsec=0.97,ok=false
|
||||
GENMD_EOF
|
||||
|
||||
etc. and I just log them as needed. Then later, I can use ``grep``, ``mlr --opprint group-like``, etc.
|
||||
etc. and I just log them as needed. Then later, I can use `grep`, `mlr --opprint group-like`, etc.
|
||||
to analyze my logs.
|
||||
|
||||
See :doc:`reference-main-io-options` regarding how to specify separators other than the default equals-sign and comma.
|
||||
|
||||
.. _file-formats-nidx:
|
||||
See [Reference: I/O options](reference-main-io-options.md) regarding how to specify separators other than the default equals-sign and comma.
|
||||
|
||||
## NIDX: Index-numbered (toolkit style)
|
||||
|
||||
With ``--inidx --ifs ' ' --repifs``, Miller splits lines on whitespace and assigns integer field names starting with 1.
|
||||
With `--inidx --ifs ' ' --repifs`, Miller splits lines on whitespace and assigns integer field names starting with 1.
|
||||
|
||||
This recapitulates Unix-toolkit behavior.
|
||||
|
||||
|
|
@ -130,9 +124,7 @@ GENMD_RUN_COMMAND
|
|||
mlr --nidx --fs ' ' --repifs cut -f 2,3 data/mydata.txt
|
||||
GENMD_EOF
|
||||
|
||||
.. _file-formats-json:
|
||||
|
||||
## Tabular JSON
|
||||
## JSON
|
||||
|
||||
JSON is a format which supports arbitrarily deep nesting of "objects" (hashmaps) and "arrays" (lists), while Miller is a tool for handling **tabular data** only. This means Miller cannot (and should not) handle arbitrary JSON. (Check out [jq](https://stedolan.github.io/jq/).)
|
||||
|
||||
|
|
@ -168,7 +160,7 @@ GENMD_RUN_COMMAND
|
|||
mlr --ijson --opprint head -n 4 data/json-example-2.json
|
||||
GENMD_EOF
|
||||
|
||||
Note in particular that as far as Miller's ``put`` and ``filter``, as well as other I/O formats, are concerned, these are simply field names with colons in them:
|
||||
Note in particular that as far as Miller's `put` and `filter`, as well as other I/O formats, are concerned, these are simply field names with colons in them:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --json --jvstack head -n 1 \
|
||||
|
|
@ -178,7 +170,7 @@ GENMD_EOF
|
|||
|
||||
### Arrays
|
||||
|
||||
Arrays (TODO: update for Miller6) aren't supported in Miller's ``put``/``filter`` DSL. By default, JSON arrays are read in as integer-keyed maps.
|
||||
Arrays (TODO: update for Miller6) aren't supported in Miller's `put`/`filter` DSL. By default, JSON arrays are read in as integer-keyed maps.
|
||||
|
||||
Suppose we have arrays like this in our input data:
|
||||
|
||||
|
|
@ -200,28 +192,26 @@ GENMD_EOF
|
|||
|
||||
This is non-ideal, but it allows Miller (5.x release being latest as of this writing) to handle JSON arrays at all.
|
||||
|
||||
You might also use ``mlr --json-skip-arrays-on-input`` or ``mlr --json-fatal-arrays-on-input``.
|
||||
You might also use `mlr --json-skip-arrays-on-input` or `mlr --json-fatal-arrays-on-input`.
|
||||
|
||||
To truly handle JSON, please use a JSON-processing tool such as [jq](https://stedolan.github.io/jq/).
|
||||
|
||||
### Formatting JSON options
|
||||
|
||||
JSON isn't a parameterized format, so ``RS``, ``FS``, ``PS`` aren't specifiable. Nonetheless, you can do the following:
|
||||
JSON isn't a parameterized format, so `RS`, `FS`, `PS` aren't specifiable. Nonetheless, you can do the following:
|
||||
|
||||
* Use ``--jvstack`` to pretty-print JSON objects with multi-line (vertically stacked) spacing. By default, each Miller record (JSON object) is one per line.
|
||||
* Use `--jvstack` to pretty-print JSON objects with multi-line (vertically stacked) spacing. By default, each Miller record (JSON object) is one per line.
|
||||
|
||||
* Keystroke-savers: ``--jsonx`` simply means ``--json --jvstack``, and ``--ojsonx`` simply means ``--ojson --jvstack``.
|
||||
* Keystroke-savers: `--jsonx` simply means `--json --jvstack`, and `--ojsonx` simply means `--ojson --jvstack`.
|
||||
|
||||
* Use ``--jlistwrap`` to print the sequence of JSON objects wrapped in an outermost ``[`` and ``]``. By default, these aren't printed.
|
||||
* Use `--jlistwrap` to print the sequence of JSON objects wrapped in an outermost `[` and `]`. By default, these aren't printed.
|
||||
|
||||
* Use ``--jquoteall`` to double-quote all object values. By default, integers, floating-point numbers, and booleans ``true`` and ``false`` are not double-quoted when they appear as JSON-object keys.
|
||||
* Use `--jquoteall` to double-quote all object values. By default, integers, floating-point numbers, and booleans `true` and `false` are not double-quoted when they appear as JSON-object keys.
|
||||
|
||||
* Use ``--jflatsep youmd.inghere`` to specify the string used for key concatenation: this defaults to a single colon.
|
||||
* Use `--jflatsep youmd.inghere` to specify the string used for key concatenation: this defaults to a single colon.
|
||||
|
||||
Again, please see [jq](https://stedolan.github.io/jq/) for a truly powerful, JSON-specific tool.
|
||||
|
||||
.. _file-formats-pprint:
|
||||
|
||||
## PPRINT: Pretty-printed tabular
|
||||
|
||||
Miller's pretty-print format is like CSV, but column-aligned. For example, compare
|
||||
|
|
@ -234,18 +224,16 @@ GENMD_RUN_COMMAND
|
|||
mlr --opprint cat data/small
|
||||
GENMD_EOF
|
||||
|
||||
Note that while Miller is a line-at-a-time processor and retains input lines in memory only where necessary (e.g. for sort), pretty-print output requires it to accumulate all input lines (so that it can compute maximum column widths) before producing any output. This has two consequences: (a) pretty-print output won't work on ``tail -f`` contexts, where Miller will be waiting for an end-of-file marker which never arrives; (b) pretty-print output for large files is constrained by available machine memory.
|
||||
Note that while Miller is a line-at-a-time processor and retains input lines in memory only where necessary (e.g. for sort), pretty-print output requires it to accumulate all input lines (so that it can compute maximum column widths) before producing any output. This has two consequences: (a) pretty-print output won't work on `tail -f` contexts, where Miller will be waiting for an end-of-file marker which never arrives; (b) pretty-print output for large files is constrained by available machine memory.
|
||||
|
||||
See [Record Heterogeneity](record-heterogeneity.md) for how Miller handles changes of field names within a single data stream.
|
||||
|
||||
For output only (this isn't supported in the input-scanner as of 5.0.0) you can use ``--barred`` with pprint output format:
|
||||
For output only (this isn't supported in the input-scanner as of 5.0.0) you can use `--barred` with pprint output format:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --opprint --barred cat data/small
|
||||
GENMD_EOF
|
||||
|
||||
.. _file-formats-xtab:
|
||||
|
||||
## XTAB: Vertical tabular
|
||||
|
||||
This is perhaps most useful for looking a very wide and/or multi-column data which causes line-wraps on the screen (but see also
|
||||
|
|
@ -273,7 +261,7 @@ As of Miller 4.3.0, markdown format is supported only for output, not input.
|
|||
|
||||
## Data-conversion keystroke-savers
|
||||
|
||||
While you can do format conversion using ``mlr --icsv --ojson cat myfile.csv``, there are also keystroke-savers for this purpose, such as ``mlr --c2j cat myfile.csv``. For a complete list:
|
||||
While you can do format conversion using `mlr --icsv --ojson cat myfile.csv`, there are also keystroke-savers for this purpose, such as `mlr --c2j cat myfile.csv`. For a complete list:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr help format-conversion
|
||||
|
|
@ -281,17 +269,17 @@ GENMD_EOF
|
|||
|
||||
## Autodetect of line endings
|
||||
|
||||
Default line endings (``--irs`` and ``--ors``) are ``'auto'`` which means **autodetect from the input file format**, as long as the input file(s) have lines ending in either LF (also known as linefeed, ``'\n'``, ``0x0a``, Unix-style) or CRLF (also known as carriage-return/linefeed pairs, ``'\r\n'``, ``0x0d 0x0a``, Windows style).
|
||||
Default line endings (`--irs` and `--ors`) are `'auto'` which means **autodetect from the input file format**, as long as the input file(s) have lines ending in either LF (also known as linefeed, `'\n'`, `0x0a`, Unix-style) or CRLF (also known as carriage-return/linefeed pairs, `'\r\n'`, `0x0d 0x0a`, Windows style).
|
||||
|
||||
**If both IRS and ORS are auto (which is the default) then LF input will lead to LF output and CRLF input will lead to CRLF output, regardless of the platform you're running on.**
|
||||
|
||||
The line-ending autodetector triggers on the first line ending detected in the input stream. E.g. if you specify a CRLF-terminated file on the command line followed by an LF-terminated file then autodetected line endings will be CRLF.
|
||||
|
||||
If you use ``--ors {something else}`` with (default or explicitly specified) ``--irs auto`` then line endings are autodetected on input and set to what you specify on output.
|
||||
If you use `--ors {something else}` with (default or explicitly specified) `--irs auto` then line endings are autodetected on input and set to what you specify on output.
|
||||
|
||||
If you use ``--irs {something else}`` with (default or explicitly specified) ``--ors auto`` then the output line endings used are LF on Unix/Linux/BSD/MacOSX, and CRLF on Windows.
|
||||
If you use `--irs {something else}` with (default or explicitly specified) `--ors auto` then the output line endings used are LF on Unix/Linux/BSD/MacOSX, and CRLF on Windows.
|
||||
|
||||
See also :ref:`reference-separators` for more information about record/field/pair separators.
|
||||
See also [Record/field/pair separators](reference-main-io-options.md#recordfieldpair-separators) for more information about record/field/pair separators.
|
||||
|
||||
## Comments in data
|
||||
|
||||
|
|
|
|||
|
|
@ -132,16 +132,22 @@ end
|
|||
|
||||
# ----------------------------------------------------------------
|
||||
def write_card(command_lines, output_lines, output_handle)
|
||||
output_handle.puts("<pre>")
|
||||
|
||||
command_lines.each do |command_line|
|
||||
output_handle.puts("<b>"+command_line+"</b>")
|
||||
if command_lines.length > 0
|
||||
output_handle.puts('<pre class="pre-highlight">')
|
||||
command_lines.each do |command_line|
|
||||
output_handle.puts("<b>"+command_line+"</b>")
|
||||
end
|
||||
output_handle.puts("</pre>")
|
||||
end
|
||||
|
||||
output_lines.each do |output_line|
|
||||
output_handle.puts(output_line)
|
||||
if output_lines.length > 0
|
||||
output_handle.puts('<pre class="pre-non-highlight">')
|
||||
output_lines.each do |output_line|
|
||||
output_handle.puts(output_line)
|
||||
end
|
||||
output_handle.puts("</pre>")
|
||||
end
|
||||
output_handle.puts("</pre>")
|
||||
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@ In several senses, Miller is more than one tool:
|
|||
**Format conversion:** You can convert CSV files to JSON, or vice versa, or
|
||||
pretty-print your data horizontally or vertically to make it easier to read.
|
||||
|
||||
**Data manipulation:** With a few keystrokes you can remove columns you don't care about -- or, make new ones using expressions like ``$rate = $units / $seconds``.
|
||||
**Data manipulation:** With a few keystrokes you can remove columns you don't care about -- or, make new ones using expressions like `$rate = $units / $seconds`.
|
||||
|
||||
**Pre-processing/post-processing vs standalone use:** You can use Miller to clean data files and put them into standard formats, perhaps in preparation to load them into a database or a hands-off data-processing pipeline. Or you can use it post-process and summary database-query output. As well, you can use Miller to explore and analyze your data interactively.
|
||||
|
||||
**Compact verbs vs programming language:** For low-keystroking you can do things like ``mlr --csv sort -f name input.csv`` or ``mlr --json head -n 1 myfile.json``. The ``sort``, ``head``, etc are called *verbs*. They're analogs of familiar command-line tools like ``sort``, ``head``, and so on -- but they're aware of name-indexed, multi-line file formats like CSV and JSON. In addition, though, using Miller's ``put`` verb you can use programming-language statements for expressions like ``$rate = $units / $seconds`` which allow you to succintly express your own logic.
|
||||
**Compact verbs vs programming language:** For low-keystroking you can do things like `mlr --csv sort -f name input.csv` or `mlr --json head -n 1 myfile.json`. The `sort`, `head`, etc are called *verbs*. They're analogs of familiar command-line tools like `sort`, `head`, and so on -- but they're aware of name-indexed, multi-line file formats like CSV and JSON. In addition, though, using Miller's `put` verb you can use programming-language statements for expressions like `$rate = $units / $seconds` which allow you to succintly express your own logic.
|
||||
|
||||
**Multiple domains:** People use Miller for data analysis, data science, software engineering, devops/system-administration, journalism, scientific research, and more.
|
||||
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@ In several senses, Miller is more than one tool:
|
|||
**Format conversion:** You can convert CSV files to JSON, or vice versa, or
|
||||
pretty-print your data horizontally or vertically to make it easier to read.
|
||||
|
||||
**Data manipulation:** With a few keystrokes you can remove columns you don't care about -- or, make new ones using expressions like ``$rate = $units / $seconds``.
|
||||
**Data manipulation:** With a few keystrokes you can remove columns you don't care about -- or, make new ones using expressions like `$rate = $units / $seconds`.
|
||||
|
||||
**Pre-processing/post-processing vs standalone use:** You can use Miller to clean data files and put them into standard formats, perhaps in preparation to load them into a database or a hands-off data-processing pipeline. Or you can use it post-process and summary database-query output. As well, you can use Miller to explore and analyze your data interactively.
|
||||
|
||||
**Compact verbs vs programming language:** For low-keystroking you can do things like ``mlr --csv sort -f name input.csv`` or ``mlr --json head -n 1 myfile.json``. The ``sort``, ``head``, etc are called *verbs*. They're analogs of familiar command-line tools like ``sort``, ``head``, and so on -- but they're aware of name-indexed, multi-line file formats like CSV and JSON. In addition, though, using Miller's ``put`` verb you can use programming-language statements for expressions like ``$rate = $units / $seconds`` which allow you to succintly express your own logic.
|
||||
**Compact verbs vs programming language:** For low-keystroking you can do things like `mlr --csv sort -f name input.csv` or `mlr --json head -n 1 myfile.json`. The `sort`, `head`, etc are called *verbs*. They're analogs of familiar command-line tools like `sort`, `head`, and so on -- but they're aware of name-indexed, multi-line file formats like CSV and JSON. In addition, though, using Miller's `put` verb you can use programming-language statements for expressions like `$rate = $units / $seconds` which allow you to succintly express your own logic.
|
||||
|
||||
**Multiple domains:** People use Miller for data analysis, data science, software engineering, devops/system-administration, journalism, scientific research, and more.
|
||||
|
||||
|
|
|
|||
|
|
@ -9,33 +9,33 @@ Until then, please see the following sections for how to get Miller 6.*
|
|||
|
||||
[Homebrew](https://brew.sh/) installation support for OSX is available via
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>brew update && brew install miller</b>
|
||||
</pre>
|
||||
|
||||
...and also via [MacPorts](https://www.macports.org/):
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>sudo port selfupdate && sudo port install miller</b>
|
||||
</pre>
|
||||
|
||||
You may already have the ``mlr`` executable available in your platform's package manager on NetBSD, Debian Linux, Ubuntu Xenial and upward, Arch Linux, or perhaps other distributions. For example, on various Linux distributions you might do one of the following:
|
||||
You may already have the `mlr` executable available in your platform's package manager on NetBSD, Debian Linux, Ubuntu Xenial and upward, Arch Linux, or perhaps other distributions. For example, on various Linux distributions you might do one of the following:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>sudo apt-get install miller</b>
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>sudo apt install miller</b>
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>sudo yum install miller</b>
|
||||
</pre>
|
||||
|
||||
On Windows, Miller is available via [Chocolatey](https://chocolatey.org/):
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>choco install miller</b>
|
||||
</pre>
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ GENMD_CARDIFY_HIGHLIGHT_ONE
|
|||
sudo port selfupdate && sudo port install miller
|
||||
GENMD_EOF
|
||||
|
||||
You may already have the ``mlr`` executable available in your platform's package manager on NetBSD, Debian Linux, Ubuntu Xenial and upward, Arch Linux, or perhaps other distributions. For example, on various Linux distributions you might do one of the following:
|
||||
You may already have the `mlr` executable available in your platform's package manager on NetBSD, Debian Linux, Ubuntu Xenial and upward, Arch Linux, or perhaps other distributions. For example, on various Linux distributions you might do one of the following:
|
||||
|
||||
GENMD_CARDIFY_HIGHLIGHT_ONE
|
||||
sudo apt-get install miller
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ Miller handles ASCII and UTF-8 strings. (I have no plans to support UTF-16 or IS
|
|||
Support for internationalization includes:
|
||||
|
||||
* Tabular output formats such pprint and xtab (see [File Formats](file-formats.md)) are aligned correctly.
|
||||
* The :ref:`reference-dsl-strlen` function correctly counts UTF-8 codepoints rather than bytes.
|
||||
* The :ref:`reference-dsl-toupper`, :ref:`reference-dsl-tolower`, and :ref:`reference-dsl-capitalize` DSL functions operate within the capabilities of the Go libraries.
|
||||
* The [strlen](reference-dsl-builtin-functions.md#strlen) function correctly counts UTF-8 codepoints rather than bytes.
|
||||
* The [toupper](reference-dsl-builtin-functions.md#toupper), [tolower](reference-dsl-builtin-functions.md#tolower), and [capitalize](reference-dsl-builtin-functions.md#capitalize) DSL functions operate within the capabilities of the Go libraries.
|
||||
|
||||
Please file an issue at https://github.com/johnkerl/miller if you encounter bugs related to internationalization (or anything else for that matter).
|
||||
Please file an issue at [https://github.com/johnkerl/miller](https://github.com/johnkerl/miller) if you encounter bugs related to internationalization (or anything else for that matter).
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ Miller handles ASCII and UTF-8 strings. (I have no plans to support UTF-16 or IS
|
|||
Support for internationalization includes:
|
||||
|
||||
* Tabular output formats such pprint and xtab (see [File Formats](file-formats.md)) are aligned correctly.
|
||||
* The :ref:`reference-dsl-strlen` function correctly counts UTF-8 codepoints rather than bytes.
|
||||
* The :ref:`reference-dsl-toupper`, :ref:`reference-dsl-tolower`, and :ref:`reference-dsl-capitalize` DSL functions operate within the capabilities of the Go libraries.
|
||||
* The [strlen](reference-dsl-builtin-functions.md#strlen) function correctly counts UTF-8 codepoints rather than bytes.
|
||||
* The [toupper](reference-dsl-builtin-functions.md#toupper), [tolower](reference-dsl-builtin-functions.md#tolower), and [capitalize](reference-dsl-builtin-functions.md#capitalize) DSL functions operate within the capabilities of the Go libraries.
|
||||
|
||||
Please file an issue at https://github.com/johnkerl/miller if you encounter bugs related to internationalization (or anything else for that matter).
|
||||
Please file an issue at [https://github.com/johnkerl/miller](https://github.com/johnkerl/miller) if you encounter bugs related to internationalization (or anything else for that matter).
|
||||
|
|
|
|||
|
|
@ -5,18 +5,22 @@
|
|||
|
||||
**This section describes behavior before Miller 5.1.0. As of 5.1.0, -u is the default.**
|
||||
|
||||
For example, the right file here has nine records, and the left file should add in the ``hostname`` column -- so the join output should also have 9 records:
|
||||
For example, the right file here has nine records, and the left file should add in the `hostname` column -- so the join output should also have 9 records:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsvlite --opprint cat data/join-u-left.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
hostname ipaddr
|
||||
nadir.east.our.org 10.3.1.18
|
||||
zenith.west.our.org 10.3.1.27
|
||||
apoapsis.east.our.org 10.4.5.94
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsvlite --opprint cat data/join-u-right.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
ipaddr timestamp bytes
|
||||
10.3.1.27 1448762579 4568
|
||||
10.3.1.18 1448762578 8729
|
||||
|
|
@ -29,8 +33,10 @@ ipaddr timestamp bytes
|
|||
10.4.5.94 1448762599 12200
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsvlite --opprint join -s -j ipaddr -f data/join-u-left.csv data/join-u-right.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
ipaddr hostname timestamp bytes
|
||||
10.3.1.27 zenith.west.our.org 1448762579 4568
|
||||
10.4.5.94 apoapsis.east.our.org 1448762579 17445
|
||||
|
|
@ -38,12 +44,14 @@ ipaddr hostname timestamp bytes
|
|||
10.4.5.94 apoapsis.east.our.org 1448762599 12200
|
||||
</pre>
|
||||
|
||||
The issue is that Miller's ``join``, by default (before 5.1.0), took input sorted (lexically ascending) by the sort keys on both the left and right files. This design decision was made intentionally to parallel the Unix/Linux system ``join`` command, which has the same semantics. The benefit of this default is that the joiner program can stream through the left and right files, needing to load neither entirely into memory. The drawback, of course, is that is requires sorted input.
|
||||
The issue is that Miller's `join`, by default (before 5.1.0), took input sorted (lexically ascending) by the sort keys on both the left and right files. This design decision was made intentionally to parallel the Unix/Linux system `join` command, which has the same semantics. The benefit of this default is that the joiner program can stream through the left and right files, needing to load neither entirely into memory. The drawback, of course, is that is requires sorted input.
|
||||
|
||||
The solution (besides pre-sorting the input files on the join keys) is to simply use **mlr join -u** (which is now the default). This loads the left file entirely into memory (while the right file is still streamed one line at a time) and does all possible joins without requiring sorted input:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsvlite --opprint join -u -j ipaddr -f data/join-u-left.csv data/join-u-right.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
ipaddr hostname timestamp bytes
|
||||
10.3.1.27 zenith.west.our.org 1448762579 4568
|
||||
10.3.1.18 nadir.east.our.org 1448762578 8729
|
||||
|
|
@ -62,14 +70,14 @@ General advice is to make sure the left-file is relatively small, e.g. containin
|
|||
|
||||
Suppose you have the following two data files:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
id,code
|
||||
3,0000ff
|
||||
2,00ff00
|
||||
4,ff0000
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
id,color
|
||||
4,red
|
||||
2,green
|
||||
|
|
@ -77,17 +85,21 @@ id,color
|
|||
|
||||
Joining on color the results are as expected:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --csv join -j id -f data/color-codes.csv data/color-names.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
id,code,color
|
||||
4,ff0000,red
|
||||
2,00ff00,green
|
||||
</pre>
|
||||
|
||||
However, if we ask for left-unpaireds, since there's no ``color`` column, we get a row not having the same column names as the other:
|
||||
However, if we ask for left-unpaireds, since there's no `color` column, we get a row not having the same column names as the other:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --csv join --ul -j id -f data/color-codes.csv data/color-names.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
id,code,color
|
||||
4,ff0000,red
|
||||
2,00ff00,green
|
||||
|
|
@ -98,10 +110,12 @@ id,code
|
|||
|
||||
To fix this, we can use **unsparsify**:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --csv join --ul -j id -f data/color-codes.csv \</b>
|
||||
<b> then unsparsify --fill-with "" \</b>
|
||||
<b> data/color-names.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
id,code,color
|
||||
4,ff0000,red
|
||||
2,00ff00,green
|
||||
|
|
@ -114,8 +128,10 @@ Thanks to @aborruso for the tip!
|
|||
|
||||
Suppose we have the following data:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat multi-join/input.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
id,task
|
||||
10,chop
|
||||
20,puree
|
||||
|
|
@ -127,30 +143,36 @@ id,task
|
|||
30,clean
|
||||
</pre>
|
||||
|
||||
And we want to augment the ``id`` column with lookups from the following data files:
|
||||
And we want to augment the `id` column with lookups from the following data files:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat multi-join/name-lookup.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
id,name
|
||||
30,Alice
|
||||
10,Bob
|
||||
20,Carol
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat multi-join/status-lookup.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
id,status
|
||||
30,occupied
|
||||
10,idle
|
||||
20,idle
|
||||
</pre>
|
||||
|
||||
We can run the input file through multiple ``join`` commands in a ``then``-chain:
|
||||
We can run the input file through multiple `join` commands in a `then`-chain:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsv --opprint join -f multi-join/name-lookup.csv -j id \</b>
|
||||
<b> then join -f multi-join/status-lookup.csv -j id \</b>
|
||||
<b> multi-join/input.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
id status name task
|
||||
10 idle Bob chop
|
||||
20 idle Carol puree
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
**This section describes behavior before Miller 5.1.0. As of 5.1.0, -u is the default.**
|
||||
|
||||
For example, the right file here has nine records, and the left file should add in the ``hostname`` column -- so the join output should also have 9 records:
|
||||
For example, the right file here has nine records, and the left file should add in the `hostname` column -- so the join output should also have 9 records:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --icsvlite --opprint cat data/join-u-left.csv
|
||||
|
|
@ -18,7 +18,7 @@ GENMD_RUN_COMMAND
|
|||
mlr --icsvlite --opprint join -s -j ipaddr -f data/join-u-left.csv data/join-u-right.csv
|
||||
GENMD_EOF
|
||||
|
||||
The issue is that Miller's ``join``, by default (before 5.1.0), took input sorted (lexically ascending) by the sort keys on both the left and right files. This design decision was made intentionally to parallel the Unix/Linux system ``join`` command, which has the same semantics. The benefit of this default is that the joiner program can stream through the left and right files, needing to load neither entirely into memory. The drawback, of course, is that is requires sorted input.
|
||||
The issue is that Miller's `join`, by default (before 5.1.0), took input sorted (lexically ascending) by the sort keys on both the left and right files. This design decision was made intentionally to parallel the Unix/Linux system `join` command, which has the same semantics. The benefit of this default is that the joiner program can stream through the left and right files, needing to load neither entirely into memory. The drawback, of course, is that is requires sorted input.
|
||||
|
||||
The solution (besides pre-sorting the input files on the join keys) is to simply use **mlr join -u** (which is now the default). This loads the left file entirely into memory (while the right file is still streamed one line at a time) and does all possible joins without requiring sorted input:
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ GENMD_RUN_COMMAND
|
|||
mlr --csv join -j id -f data/color-codes.csv data/color-names.csv
|
||||
GENMD_EOF
|
||||
|
||||
However, if we ask for left-unpaireds, since there's no ``color`` column, we get a row not having the same column names as the other:
|
||||
However, if we ask for left-unpaireds, since there's no `color` column, we get a row not having the same column names as the other:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --csv join --ul -j id -f data/color-codes.csv data/color-names.csv
|
||||
|
|
@ -66,7 +66,7 @@ GENMD_RUN_COMMAND
|
|||
cat multi-join/input.csv
|
||||
GENMD_EOF
|
||||
|
||||
And we want to augment the ``id`` column with lookups from the following data files:
|
||||
And we want to augment the `id` column with lookups from the following data files:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
cat multi-join/name-lookup.csv
|
||||
|
|
@ -76,7 +76,7 @@ GENMD_RUN_COMMAND
|
|||
cat multi-join/status-lookup.csv
|
||||
GENMD_EOF
|
||||
|
||||
We can run the input file through multiple ``join`` commands in a ``then``-chain:
|
||||
We can run the input file through multiple `join` commands in a `then`-chain:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --icsv --opprint join -f multi-join/name-lookup.csv -j id \
|
||||
|
|
|
|||
|
|
@ -3,17 +3,21 @@
|
|||
|
||||
## Short format specifiers
|
||||
|
||||
In our examples so far we've often made use of ``mlr --icsv --opprint`` or ``mlr --icsv --ojson``. These are such frequently occurring patterns that they have short options like **--c2p** and **--c2j**:
|
||||
In our examples so far we've often made use of `mlr --icsv --opprint` or `mlr --icsv --ojson`. These are such frequently occurring patterns that they have short options like **--c2p** and **--c2j**:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --c2p head -n 2 example.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color shape flag index quantity rate
|
||||
yellow triangle true 11 43.6498 9.8870
|
||||
red square true 15 79.2778 0.0130
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --c2j head -n 2 example.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
{
|
||||
"color": "yellow",
|
||||
"shape": "triangle",
|
||||
|
|
@ -36,32 +40,36 @@ You can get the full list here (TODO:linkify).
|
|||
|
||||
## File names up front
|
||||
|
||||
Already we saw that you can put the filename first using ``--from``. When you're interacting with your data at the command line, this makes it easier to up-arrow and append to the previous command:
|
||||
Already we saw that you can put the filename first using `--from`. When you're interacting with your data at the command line, this makes it easier to up-arrow and append to the previous command:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --c2p --from example.csv sort -nr index then head -n 3</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color shape flag index quantity rate
|
||||
purple square false 91 72.3735 8.2430
|
||||
yellow circle true 87 63.5058 8.3350
|
||||
yellow circle true 73 63.9785 4.2370
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --c2p --from example.csv sort -nr index then head -n 3 then cut -f shape,quantity</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
shape quantity
|
||||
square 72.3735
|
||||
circle 63.5058
|
||||
circle 63.9785
|
||||
</pre>
|
||||
|
||||
If there's more than one input file, you can use ``--mfrom``, then however many file names, then ``--`` to indicate the end of your input-file-name list:
|
||||
If there's more than one input file, you can use `--mfrom`, then however many file names, then `--` to indicate the end of your input-file-name list:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --c2p --mfrom data/*.csv -- sort -n index</b>
|
||||
</pre>
|
||||
|
||||
## .mlrrc file
|
||||
|
||||
If you want the default file format for Miller to be CSV you can simply put ``--csv`` on a line by itself in your ``~/.mlrrc`` file. Then instead of ``mlr --csv cat example.csv`` you can just do ``mlr cat example.csv``. This is just the default, though, so ``mlr --opprint cat example.csv`` will still use default CSV format for input, and PPRINT (tabular) for output.
|
||||
If you want the default file format for Miller to be CSV you can simply put `--csv` on a line by itself in your `~/.mlrrc` file. Then instead of `mlr --csv cat example.csv` you can just do `mlr cat example.csv`. This is just the default, though, so `mlr --opprint cat example.csv` will still use default CSV format for input, and PPRINT (tabular) for output.
|
||||
|
||||
You can read more about this at the [Customization](customization.md) page.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
## Short format specifiers
|
||||
|
||||
In our examples so far we've often made use of ``mlr --icsv --opprint`` or ``mlr --icsv --ojson``. These are such frequently occurring patterns that they have short options like **--c2p** and **--c2j**:
|
||||
In our examples so far we've often made use of `mlr --icsv --opprint` or `mlr --icsv --ojson`. These are such frequently occurring patterns that they have short options like **--c2p** and **--c2j**:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --c2p head -n 2 example.csv
|
||||
|
|
@ -16,7 +16,7 @@ You can get the full list here (TODO:linkify).
|
|||
|
||||
## File names up front
|
||||
|
||||
Already we saw that you can put the filename first using ``--from``. When you're interacting with your data at the command line, this makes it easier to up-arrow and append to the previous command:
|
||||
Already we saw that you can put the filename first using `--from`. When you're interacting with your data at the command line, this makes it easier to up-arrow and append to the previous command:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --c2p --from example.csv sort -nr index then head -n 3
|
||||
|
|
@ -26,7 +26,7 @@ GENMD_RUN_COMMAND
|
|||
mlr --c2p --from example.csv sort -nr index then head -n 3 then cut -f shape,quantity
|
||||
GENMD_EOF
|
||||
|
||||
If there's more than one input file, you can use ``--mfrom``, then however many file names, then ``--`` to indicate the end of your input-file-name list:
|
||||
If there's more than one input file, you can use `--mfrom`, then however many file names, then `--` to indicate the end of your input-file-name list:
|
||||
|
||||
GENMD_SHOW_COMMAND
|
||||
mlr --c2p --mfrom data/*.csv -- sort -n index
|
||||
|
|
@ -34,6 +34,6 @@ GENMD_EOF
|
|||
|
||||
## .mlrrc file
|
||||
|
||||
If you want the default file format for Miller to be CSV you can simply put ``--csv`` on a line by itself in your ``~/.mlrrc`` file. Then instead of ``mlr --csv cat example.csv`` you can just do ``mlr cat example.csv``. This is just the default, though, so ``mlr --opprint cat example.csv`` will still use default CSV format for input, and PPRINT (tabular) for output.
|
||||
If you want the default file format for Miller to be CSV you can simply put `--csv` on a line by itself in your `~/.mlrrc` file. Then instead of `mlr --csv cat example.csv` you can just do `mlr cat example.csv`. This is just the default, though, so `mlr --opprint cat example.csv` will still use default CSV format for input, and PPRINT (tabular) for output.
|
||||
|
||||
You can read more about this at the [Customization](customization.md) page.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<!--- PLEASE DO NOT EDIT DIRECTLY. EDIT THE .md.in FILE PLEASE. --->
|
||||
# Log-processing examples
|
||||
|
||||
Another of my favorite use-cases for Miller is doing ad-hoc processing of log-file data. Here's where DKVP format really shines: one, since the field names and field values are present on every line, every line stands on its own. That means you can ``grep`` or what have you. Also it means not every line needs to have the same list of field names ("schema").
|
||||
Another of my favorite use-cases for Miller is doing ad-hoc processing of log-file data. Here's where DKVP format really shines: one, since the field names and field values are present on every line, every line stands on its own. That means you can `grep` or what have you. Also it means not every line needs to have the same list of field names ("schema").
|
||||
|
||||
## Generating and aggregating log-file output
|
||||
|
||||
|
|
@ -11,8 +11,10 @@ Writing a program -- in any language whatsoever -- you can have it print out log
|
|||
|
||||
Suppose your program has printed something like this [log.txt](./log.txt):
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat log.txt</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
op=enter,time=1472819681
|
||||
op=cache,type=A9,hit=0
|
||||
op=cache,type=A4,hit=1
|
||||
|
|
@ -58,22 +60,26 @@ op=cache,type=A9,hit=0
|
|||
time=1472819742,batch_size=100,num_filtered=728
|
||||
</pre>
|
||||
|
||||
Each print statement simply contains local information: the current timestamp, whether a particular cache was hit or not, etc. Then using either the system ``grep`` command, or Miller's ``having-fields``, or ``is_present``, we can pick out the parts we want and analyze them:
|
||||
Each print statement simply contains local information: the current timestamp, whether a particular cache was hit or not, etc. Then using either the system `grep` command, or Miller's `having-fields`, or `is_present`, we can pick out the parts we want and analyze them:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>grep op=cache log.txt \</b>
|
||||
<b> | mlr --idkvp --opprint stats1 -a mean -f hit -g type then sort -f type</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
type hit_mean
|
||||
A1 0.8571428571428571
|
||||
A4 0.7142857142857143
|
||||
A9 0.09090909090909091
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from log.txt --opprint \</b>
|
||||
<b> filter 'is_present($batch_size)' \</b>
|
||||
<b> then step -a delta -f time,num_filtered \</b>
|
||||
<b> then sec2gmt time</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
time batch_size num_filtered time_delta num_filtered_delta
|
||||
2016-09-02T12:34:50Z 100 237 0 0
|
||||
2016-09-02T12:35:05Z 100 348 15 111
|
||||
|
|
@ -85,8 +91,10 @@ time batch_size num_filtered time_delta num_filtered_delta
|
|||
|
||||
Alternatively, we can simply group the similar data for a better look:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --opprint group-like log.txt</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
op time
|
||||
enter 1472819681
|
||||
|
||||
|
|
@ -137,8 +145,10 @@ time batch_size num_filtered
|
|||
1472819742 100 728
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --opprint group-like then sec2gmt time log.txt</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
op time
|
||||
enter 2016-09-02T12:34:41Z
|
||||
|
||||
|
|
@ -193,13 +203,13 @@ time batch_size num_filtered
|
|||
|
||||
This, of course, depends highly on what's in your log files. But, as an example, suppose you have log-file lines such as
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
2015-10-08 08:29:09,445 INFO com.company.path.to.ClassName @ [sometext] various/sorts/of data {& punctuation} hits=1 status=0 time=2.378
|
||||
</pre>
|
||||
|
||||
I prefer to pre-filter with ``grep`` and/or ``sed`` to extract the structured text, then hand that to Miller. Example:
|
||||
I prefer to pre-filter with `grep` and/or `sed` to extract the structured text, then hand that to Miller. Example:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>grep 'various sorts' *.log \</b>
|
||||
<b> | sed 's/.*} //' \</b>
|
||||
<b> | mlr --fs space --repifs --oxtab stats1 -a min,p10,p50,p90,max -f time -g status</b>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Log-processing examples
|
||||
|
||||
Another of my favorite use-cases for Miller is doing ad-hoc processing of log-file data. Here's where DKVP format really shines: one, since the field names and field values are present on every line, every line stands on its own. That means you can ``grep`` or what have you. Also it means not every line needs to have the same list of field names ("schema").
|
||||
Another of my favorite use-cases for Miller is doing ad-hoc processing of log-file data. Here's where DKVP format really shines: one, since the field names and field values are present on every line, every line stands on its own. That means you can `grep` or what have you. Also it means not every line needs to have the same list of field names ("schema").
|
||||
|
||||
## Generating and aggregating log-file output
|
||||
|
||||
|
|
@ -14,7 +14,7 @@ GENMD_RUN_COMMAND
|
|||
cat log.txt
|
||||
GENMD_EOF
|
||||
|
||||
Each print statement simply contains local information: the current timestamp, whether a particular cache was hit or not, etc. Then using either the system ``grep`` command, or Miller's ``having-fields``, or ``is_present``, we can pick out the parts we want and analyze them:
|
||||
Each print statement simply contains local information: the current timestamp, whether a particular cache was hit or not, etc. Then using either the system `grep` command, or Miller's `having-fields`, or `is_present`, we can pick out the parts we want and analyze them:
|
||||
|
||||
GENMD_INCLUDE_AND_RUN_ESCAPED(10-1.sh)
|
||||
|
||||
|
|
@ -38,7 +38,7 @@ GENMD_CARDIFY
|
|||
2015-10-08 08:29:09,445 INFO com.company.path.to.ClassName @ [sometext] various/sorts/of data {& punctuation} hits=1 status=0 time=2.378
|
||||
GENMD_EOF
|
||||
|
||||
I prefer to pre-filter with ``grep`` and/or ``sed`` to extract the structured text, then hand that to Miller. Example:
|
||||
I prefer to pre-filter with `grep` and/or `sed` to extract the structured text, then hand that to Miller. Example:
|
||||
|
||||
GENMD_SHOW_COMMAND
|
||||
grep 'various sorts' *.log \
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
This is simply a copy of what you should see on running **man mlr** at a command prompt, once Miller is installed on your system.
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
MILLER(1) MILLER(1)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -7,17 +7,17 @@ Miller was originally developed for Unix-like operating systems including Linux
|
|||
|
||||
The experience is now almost the same as on Linux, NetBSD/FreeBSD, and MacOS.
|
||||
|
||||
MSYS2 is no longer required, although you can use Miller from within MSYS2 if you like. There is now simply a single ``mlr.exe``, with no ``msys2.dll`` alongside anymore.
|
||||
MSYS2 is no longer required, although you can use Miller from within MSYS2 if you like. There is now simply a single `mlr.exe`, with no `msys2.dll` alongside anymore.
|
||||
|
||||
See [Installation](installation.md) for how to get a copy of ``mlr.exe``.
|
||||
See [Installation](installation.md) for how to get a copy of `mlr.exe`.
|
||||
|
||||
## Setup
|
||||
|
||||
Simply place ``mlr.exe`` somewhere within your ``PATH`` variable.
|
||||
Simply place `mlr.exe` somewhere within your `PATH` variable.
|
||||
|
||||

|
||||
|
||||
To use Miller from within MSYS2/Cygwin, also make sure ``mlr.exe`` is within the ``PATH`` variable.
|
||||
To use Miller from within MSYS2/Cygwin, also make sure `mlr.exe` is within the `PATH` variable.
|
||||
|
||||

|
||||
|
||||
|
|
@ -25,18 +25,18 @@ To use Miller from within MSYS2/Cygwin, also make sure ``mlr.exe`` is within the
|
|||
|
||||
[Output Colorization](output-colorization.md) doesn't work on Windows, outside of MSYS2.
|
||||
|
||||
The Windows-support code within Miller makes effort to support Linux/Unix/MacOS-like command-line syntax including single-quoting of expressions for ``mlr put`` and ``mlr filter`` -- and in the examples above, this often works. However, there are still some cases where more complex expressions aren't successfully parsed from the Windows prompt, even though they are from MSYS2:
|
||||
The Windows-support code within Miller makes effort to support Linux/Unix/MacOS-like command-line syntax including single-quoting of expressions for `mlr put` and `mlr filter` -- and in the examples above, this often works. However, there are still some cases where more complex expressions aren't successfully parsed from the Windows prompt, even though they are from MSYS2:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
Single quotes with ``&&`` or ``||`` inside are fundamentally unhandleable within Windows; there is nothing Miller can do here as the Windows command line is split before Miller ever receives it.
|
||||
Single quotes with `&&` or `||` inside are fundamentally unhandleable within Windows; there is nothing Miller can do here as the Windows command line is split before Miller ever receives it.
|
||||
|
||||
One workaround is to use MSYS2. Another workaround is to put more complex DSL expressions into a file:
|
||||
|
||||

|
||||
|
||||
A third workaround is to replace ``"`` with ``"""``, then ``'`` with ``"``:
|
||||
A third workaround is to replace `"` with `"""`, then `'` with `"`:
|
||||
|
||||

|
||||
|
|
|
|||
|
|
@ -6,17 +6,17 @@ Miller was originally developed for Unix-like operating systems including Linux
|
|||
|
||||
The experience is now almost the same as on Linux, NetBSD/FreeBSD, and MacOS.
|
||||
|
||||
MSYS2 is no longer required, although you can use Miller from within MSYS2 if you like. There is now simply a single ``mlr.exe``, with no ``msys2.dll`` alongside anymore.
|
||||
MSYS2 is no longer required, although you can use Miller from within MSYS2 if you like. There is now simply a single `mlr.exe`, with no `msys2.dll` alongside anymore.
|
||||
|
||||
See [Installation](installation.md) for how to get a copy of ``mlr.exe``.
|
||||
See [Installation](installation.md) for how to get a copy of `mlr.exe`.
|
||||
|
||||
## Setup
|
||||
|
||||
Simply place ``mlr.exe`` somewhere within your ``PATH`` variable.
|
||||
Simply place `mlr.exe` somewhere within your `PATH` variable.
|
||||
|
||||

|
||||
|
||||
To use Miller from within MSYS2/Cygwin, also make sure ``mlr.exe`` is within the ``PATH`` variable.
|
||||
To use Miller from within MSYS2/Cygwin, also make sure `mlr.exe` is within the `PATH` variable.
|
||||
|
||||

|
||||
|
||||
|
|
@ -24,18 +24,18 @@ To use Miller from within MSYS2/Cygwin, also make sure ``mlr.exe`` is within the
|
|||
|
||||
[Output Colorization](output-colorization.md) doesn't work on Windows, outside of MSYS2.
|
||||
|
||||
The Windows-support code within Miller makes effort to support Linux/Unix/MacOS-like command-line syntax including single-quoting of expressions for ``mlr put`` and ``mlr filter`` -- and in the examples above, this often works. However, there are still some cases where more complex expressions aren't successfully parsed from the Windows prompt, even though they are from MSYS2:
|
||||
The Windows-support code within Miller makes effort to support Linux/Unix/MacOS-like command-line syntax including single-quoting of expressions for `mlr put` and `mlr filter` -- and in the examples above, this often works. However, there are still some cases where more complex expressions aren't successfully parsed from the Windows prompt, even though they are from MSYS2:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
Single quotes with ``&&`` or ``||`` inside are fundamentally unhandleable within Windows; there is nothing Miller can do here as the Windows command line is split before Miller ever receives it.
|
||||
Single quotes with `&&` or `||` inside are fundamentally unhandleable within Windows; there is nothing Miller can do here as the Windows command line is split before Miller ever receives it.
|
||||
|
||||
One workaround is to use MSYS2. Another workaround is to put more complex DSL expressions into a file:
|
||||
|
||||

|
||||
|
||||
A third workaround is to replace ``"`` with ``"""``, then ``'`` with ``"``:
|
||||
A third workaround is to replace `"` with `"""`, then `'` with `"`:
|
||||
|
||||

|
||||
|
|
|
|||
|
|
@ -3,62 +3,64 @@
|
|||
|
||||
Column select:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --csv cut -f hostname,uptime mydata.csv</b>
|
||||
</pre>
|
||||
|
||||
Add new columns as function of other columns:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --nidx put '$sum = $7 < 0.0 ? 3.5 : $7 + 2.1*$8' *.dat</b>
|
||||
</pre>
|
||||
|
||||
Row filter:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --csv filter '$status != "down" && $upsec >= 10000' *.csv</b>
|
||||
</pre>
|
||||
|
||||
Apply column labels and pretty-print:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>grep -v '^#' /etc/group | mlr --ifs : --nidx --opprint label group,pass,gid,member then sort -f group</b>
|
||||
</pre>
|
||||
|
||||
Join multiple data sources on key columns:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr join -j account_id -f accounts.dat then group-by account_name balances.dat</b>
|
||||
</pre>
|
||||
|
||||
Mulltiple formats including JSON:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --json put '$attr = sub($attr, "([0-9]+)_([0-9]+)_.*", "\1:\2")' data/*.json</b>
|
||||
</pre>
|
||||
|
||||
Aggregate per-column statistics:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr stats1 -a min,mean,max,p10,p50,p90 -f flag,u,v data/*</b>
|
||||
</pre>
|
||||
|
||||
Linear regression:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr stats2 -a linreg-pca -f u,v -g shape data/*</b>
|
||||
</pre>
|
||||
|
||||
Aggregate custom per-column statistics:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put -q '@sum[$a][$b] += $x; end {emit @sum, "a", "b"}' data/*</b>
|
||||
</pre>
|
||||
|
||||
Iterate over data using DSL expressions:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from estimates.tbl put '</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
for (k,v in $*) {
|
||||
if (is_numeric(v) && k =~ "^[t-z].*$") {
|
||||
$sum += v; $count += 1
|
||||
|
|
@ -70,39 +72,39 @@ Iterate over data using DSL expressions:
|
|||
|
||||
Run DSL expressions from a script file:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from infile.dat put -f analyze.mlr</b>
|
||||
</pre>
|
||||
|
||||
Split/reduce output to multiple filenames:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from infile.dat put 'tee > "./taps/data-".$a."-".$b, $*'</b>
|
||||
</pre>
|
||||
|
||||
Compressed I/O:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from infile.dat put 'tee | "gzip > ./taps/data-".$a."-".$b.".gz", $*'</b>
|
||||
</pre>
|
||||
|
||||
Interoperate with other data-processing tools using standard pipes:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from infile.dat put -q '@v=$*; dump | "jq .[]"'</b>
|
||||
</pre>
|
||||
|
||||
Tap/trace:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from infile.dat put '(NR % 1000 == 0) { print > stderr, "Checkpoint ".NR}'</b>
|
||||
</pre>
|
||||
|
||||
## Program timing
|
||||
|
||||
This admittedly artificial example demonstrates using Miller time and stats functions to introspectively acquire some information about Miller's own runtime. The ``delta`` function computes the difference between successive timestamps.
|
||||
This admittedly artificial example demonstrates using Miller time and stats functions to introspectively acquire some information about Miller's own runtime. The `delta` function computes the difference between successive timestamps.
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
$ ruby -e '10000.times{|i|puts "i=#{i+1}"}' > lines.txt
|
||||
|
||||
$ head -n 5 lines.txt
|
||||
|
|
@ -136,8 +138,10 @@ t_delta_max 5.388259888e-05
|
|||
|
||||
Suppose you have a database query which you run at one point in time, producing the output on the left, then again later producing the output on the right:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/previous_counters.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color,count
|
||||
red,3472
|
||||
blue,6838
|
||||
|
|
@ -145,8 +149,10 @@ orange,694
|
|||
purple,12
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/current_counters.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color,count
|
||||
red,3467
|
||||
orange,670
|
||||
|
|
@ -158,12 +164,14 @@ And, suppose you want to compute the differences in the counters between adjacen
|
|||
|
||||
First, rename counter columns to make them distinct:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --csv rename count,previous_count data/previous_counters.csv > data/prevtemp.csv</b>
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/prevtemp.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color,previous_count
|
||||
red,3472
|
||||
blue,6838
|
||||
|
|
@ -171,12 +179,14 @@ orange,694
|
|||
purple,12
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --csv rename count,current_count data/current_counters.csv > data/currtemp.csv</b>
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/currtemp.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color,current_count
|
||||
red,3467
|
||||
orange,670
|
||||
|
|
@ -184,14 +194,16 @@ yellow,27
|
|||
blue,6944
|
||||
</pre>
|
||||
|
||||
Then, join on the key field(s), and use unsparsify to zero-fill counters absent on one side but present on the other. Use ``--ul`` and ``--ur`` to emit unpaired records (namely, purple on the left and yellow on the right):
|
||||
Then, join on the key field(s), and use unsparsify to zero-fill counters absent on one side but present on the other. Use `--ul` and `--ur` to emit unpaired records (namely, purple on the left and yellow on the right):
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsv --opprint \</b>
|
||||
<b> join -j color --ul --ur -f data/prevtemp.csv \</b>
|
||||
<b> then unsparsify --fill-with 0 \</b>
|
||||
<b> then put '$count_delta = $current_count - $previous_count' \</b>
|
||||
<b> data/currtemp.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color previous_count current_count count_delta
|
||||
red 3472 3467 -5
|
||||
orange 694 670 -24
|
||||
|
|
@ -200,13 +212,11 @@ blue 6838 6944 106
|
|||
purple 12 0 (error)
|
||||
</pre>
|
||||
|
||||
.. _cookbook-memoization-with-oosvars:
|
||||
|
||||
## Memoization with out-of-stream variables
|
||||
|
||||
The recursive function for the Fibonacci sequence is famous for its computational complexity. Namely, using f(0)=1, f(1)=1, f(n)=f(n-1)+f(n-2) for n>=2, the evaluation tree branches left as well as right at each non-trivial level, resulting in millions or more paths to the root 0/1 nodes for larger n. This program
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
mlr --ofmt '%.9lf' --opprint seqgen --start 1 --stop 28 then put '
|
||||
func f(n) {
|
||||
@fcount += 1; # count number of calls to the function
|
||||
|
|
@ -226,7 +236,7 @@ mlr --ofmt '%.9lf' --opprint seqgen --start 1 --stop 28 then put '
|
|||
|
||||
produces output like this:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
i o fcount seconds_delta
|
||||
1 1 1 0
|
||||
2 2 3 0.000039101
|
||||
|
|
@ -258,9 +268,9 @@ i o fcount seconds_delta
|
|||
28 514229 1028457 0.971235037
|
||||
</pre>
|
||||
|
||||
Note that the time it takes to evaluate the function is blowing up exponentially as the input argument increases. Using ``@``-variables, which persist across records, we can cache and reuse the results of previous computations:
|
||||
Note that the time it takes to evaluate the function is blowing up exponentially as the input argument increases. Using `@`-variables, which persist across records, we can cache and reuse the results of previous computations:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
mlr --ofmt '%.9lf' --opprint seqgen --start 1 --stop 28 then put '
|
||||
func f(n) {
|
||||
@fcount += 1; # count number of calls to the function
|
||||
|
|
@ -283,7 +293,7 @@ mlr --ofmt '%.9lf' --opprint seqgen --start 1 --stop 28 then put '
|
|||
|
||||
with output like this:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
i o fcount seconds_delta
|
||||
1 1 1 0
|
||||
2 2 3 0.000053883
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ GENMD_EOF
|
|||
|
||||
## Program timing
|
||||
|
||||
This admittedly artificial example demonstrates using Miller time and stats functions to introspectively acquire some information about Miller's own runtime. The ``delta`` function computes the difference between successive timestamps.
|
||||
This admittedly artificial example demonstrates using Miller time and stats functions to introspectively acquire some information about Miller's own runtime. The `delta` function computes the difference between successive timestamps.
|
||||
|
||||
GENMD_INCLUDE_ESCAPED(data/timing-example.txt)
|
||||
|
||||
|
|
@ -135,12 +135,10 @@ GENMD_RUN_COMMAND
|
|||
cat data/currtemp.csv
|
||||
GENMD_EOF
|
||||
|
||||
Then, join on the key field(s), and use unsparsify to zero-fill counters absent on one side but present on the other. Use ``--ul`` and ``--ur`` to emit unpaired records (namely, purple on the left and yellow on the right):
|
||||
Then, join on the key field(s), and use unsparsify to zero-fill counters absent on one side but present on the other. Use `--ul` and `--ur` to emit unpaired records (namely, purple on the left and yellow on the right):
|
||||
|
||||
GENMD_INCLUDE_AND_RUN_ESCAPED(data/previous-to-current.sh)
|
||||
|
||||
.. _cookbook-memoization-with-oosvars:
|
||||
|
||||
## Memoization with out-of-stream variables
|
||||
|
||||
The recursive function for the Fibonacci sequence is famous for its computational complexity. Namely, using f(0)=1, f(1)=1, f(n)=f(n-1)+f(n-2) for n>=2, the evaluation tree branches left as well as right at each non-trivial level, resulting in millions or more paths to the root 0/1 nodes for larger n. This program
|
||||
|
|
@ -181,7 +179,7 @@ i o fcount seconds_delta
|
|||
28 514229 1028457 0.971235037
|
||||
GENMD_EOF
|
||||
|
||||
Note that the time it takes to evaluate the function is blowing up exponentially as the input argument increases. Using ``@``-variables, which persist across records, we can cache and reuse the results of previous computations:
|
||||
Note that the time it takes to evaluate the function is blowing up exponentially as the input argument increases. Using `@`-variables, which persist across records, we can cache and reuse the results of previous computations:
|
||||
|
||||
GENMD_INCLUDE_ESCAPED(data/fibo-cached.sh)
|
||||
|
||||
|
|
|
|||
|
|
@ -47,16 +47,20 @@ mlr help list-functions | grep -v '^[a-zA-Z]' | uniq | while read funcname; do
|
|||
linkname='ursheq'
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo ".. _reference-dsl-${linkname}:"
|
||||
echo ""
|
||||
echo "$displayname"
|
||||
echo "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"
|
||||
echo ""
|
||||
echo '.. code-block:: none'
|
||||
# TODO: fix section-links for mnkdocs
|
||||
#echo ''
|
||||
#echo ".. _reference-dsl-${linkname}:"
|
||||
echo ''
|
||||
mlr help function "$funcname" | sed 's/^/ /'
|
||||
if [ "$linkname" = "$displayname" ]; then
|
||||
echo "## $displayname"
|
||||
else
|
||||
echo "<a id=$linkname />"
|
||||
echo "## $displayname"
|
||||
fi
|
||||
echo ''
|
||||
echo '<pre class="pre-non-highlight">'
|
||||
mlr help function "$funcname"
|
||||
echo '</pre>'
|
||||
echo ''
|
||||
done
|
||||
|
||||
|
|
@ -101,15 +105,19 @@ mlr help list-functions | grep '^[a-zA-Z]' | sort -u | while read funcname; do
|
|||
linkname='ursheq'
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo ".. _reference-dsl-${linkname}:"
|
||||
echo ""
|
||||
echo "$displayname"
|
||||
echo "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"
|
||||
echo ""
|
||||
echo '.. code-block:: none'
|
||||
# TODO: fix section-links for mnkdocs
|
||||
#echo ''
|
||||
#echo ".. _reference-dsl-${linkname}:"
|
||||
echo ''
|
||||
mlr help function "$funcname" | sed 's/^/ /'
|
||||
if [ "$linkname" = "$displayname" ]; then
|
||||
echo "## $displayname"
|
||||
else
|
||||
echo "<a id=$linkname />"
|
||||
echo "## $displayname"
|
||||
fi
|
||||
echo ''
|
||||
echo '<pre class="pre-non-highlight">'
|
||||
mlr help function "$funcname"
|
||||
echo '</pre>'
|
||||
echo ''
|
||||
done
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ See also the [list of issues tagged with go-port](https://github.com/johnkerl/mi
|
|||
|
||||
## Documentation improvements
|
||||
|
||||
Documentation (what you're reading here) and on-line help (``mlr --help``) have been completely reworked.
|
||||
Documentation (what you're reading here) and on-line help (`mlr --help`) have been completely reworked.
|
||||
|
||||
In the initial release, the focus was convincing users already familiar with
|
||||
``awk``/``grep``/``cut`` that Miller was a viable option. Over time it's become
|
||||
`awk`/`grep`/`cut` that Miller was a viable option. Over time it's become
|
||||
clear that many users aren't expert with these. The focus has shifted toward a
|
||||
higher quantity of more introductory/accessible material for command-line data
|
||||
processing.
|
||||
|
|
@ -25,14 +25,14 @@ now discussed first, and more examples use CSV.
|
|||
|
||||
## JSON support, and arrays
|
||||
|
||||
Arrays are now supported in Miller's ``put``/``filter`` programming language,
|
||||
as described at :doc:`reference-dsl-arrays`. Also, ``array`` is now a keyword
|
||||
Arrays are now supported in Miller's `put`/`filter` programming language,
|
||||
as described at [Reference: arrays](reference-dsl-arrays.md). Also, `array` is now a keyword
|
||||
so this is no longer usable as a local-variable or UDF name.
|
||||
|
||||
JSON support is improved:
|
||||
|
||||
* Direct support for arrays means that you can now use Miller to process more JSON files.
|
||||
* Streamable JSON parsing: Miller's internal record-processing pipeline starts as soon as the first record is read (which was already the case for other file formats). This means that, unless records are wrapped with outermost ``[...]``, Miller now handles JSON in ``tail -f`` contexts like it does for other file formats.
|
||||
* Streamable JSON parsing: Miller's internal record-processing pipeline starts as soon as the first record is read (which was already the case for other file formats). This means that, unless records are wrapped with outermost `[...]`, Miller now handles JSON in `tail -f` contexts like it does for other file formats.
|
||||
* Flatten/unflatten -- TODO pick a name and link to a separate page/section
|
||||
|
||||
## Improved Windows experience
|
||||
|
|
@ -44,11 +44,7 @@ Binaries are reliably available using GitHub Actions: see also [Installation](in
|
|||
|
||||
## In-process support for compressed input
|
||||
|
||||
In addition to ``--prepipe gunzip``, you can now use the ``--gzin`` flag. In
|
||||
fact, if your files end in ``.gz`` you don't even need to do that -- Miller
|
||||
will autodetect by file extension and automatically uncompress ``mlr --csv cat
|
||||
foo.csv.gz``. Similarly for ``.z`` and ``.bz2`` files. Please see section
|
||||
[TODO:linkify] for more information.
|
||||
In addition to `--prepipe gunzip`, you can now use the `--gzin` flag. In fact, if your files end in `.gz` you don't even need to do that -- Miller will autodetect by file extension and automatically uncompress `mlr --csv cat foo.csv.gz`. Similarly for `.z` and `.bz2` files. Please see section [TODO:linkify] for more information.
|
||||
|
||||
## Output colorization
|
||||
|
||||
|
|
@ -60,12 +56,12 @@ The most central part of Miller 6 is a deep refactor of how data values are pars
|
|||
from file contents, how types are inferred, and how they're converted back to
|
||||
text into output files.
|
||||
|
||||
This was all initiated by https://github.com/johnkerl/miller/issues/151.
|
||||
This was all initiated by [https://github.com/johnkerl/miller/issues/151](https://github.com/johnkerl/miller/issues/151).
|
||||
|
||||
In Miller 5 and below, all values were stored as strings, then only converted
|
||||
to int/float as-needed, for example when a particular field was referenced in
|
||||
the ``stats1`` or ``put`` verbs. This led to awkwardnesses such as the ``-S``
|
||||
and ``-F`` flags for ``put`` and ``filter``.
|
||||
the `stats1` or `put` verbs. This led to awkwardnesses such as the `-S`
|
||||
and `-F` flags for `put` and `filter`.
|
||||
|
||||
In Miller 6, things parseable as int/float are treated as such from the moment
|
||||
the input data is read, and these are passed along through the verb chain. All
|
||||
|
|
@ -75,17 +71,21 @@ a numeric field isn't modified during the processing chain, it's printed out
|
|||
the way it arrived. Also, quoted values in JSON strings are flagged as being
|
||||
strings throughout the processing chain.
|
||||
|
||||
For example (see https://github.com/johnkerl/miller/issues/178) you can now do
|
||||
For example (see [https://github.com/johnkerl/miller/issues/178](https://github.com/johnkerl/miller/issues/178)) you can now do
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>echo '{ "a": "0123" }' | mlr --json cat</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
{
|
||||
"a": "0123"
|
||||
}
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>echo '{ "x": 1.230, "y": 1.230000000 }' | mlr --json cat</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
{
|
||||
"x": 1.230,
|
||||
"y": 1.230000000
|
||||
|
|
@ -98,28 +98,30 @@ Miller now has a read-evaluate-print-loop ([REPL](repl.md)) where you can single
|
|||
|
||||
## New DSL functions / operators
|
||||
|
||||
* String-hashing functions :ref:`reference-dsl-md5`, :ref:`reference-dsl-sha1`, :ref:`reference-dsl-sha256`, and :ref:`reference-dsl-sha512`.
|
||||
* Platform-property functions :ref:`reference-dsl-hostname`, :ref:`reference-dsl-os`, and :ref:`reference-dsl-version`.
|
||||
* Unsigned right-shift :ref:`reference-dsl-ursh` along with ``>>>=``.
|
||||
* String-hashing functions [md5](reference-dsl-builtin-functions.md#md5), [sha1](reference-dsl-builtin-functions.md#sha1), [sha256](reference-dsl-builtin-functions.md#sha256), and [sha512](reference-dsl-builtin-functions.md#sha512).
|
||||
|
||||
* Platform-property functions [hostname](reference-dsl-builtin-functions.md#hostname), [os](reference-dsl-builtin-functions.md#os), and [version](reference-dsl-builtin-functions.md#version).
|
||||
|
||||
* Unsigned right-shift [`>>>`](reference-dsl-builtin-functions.md#ursh) along with `>>>=`.
|
||||
|
||||
## Improved command-line parsing
|
||||
|
||||
Miller 6 has getoptish command-line parsing (https://github.com/johnkerl/miller/pull/467):
|
||||
|
||||
* ``-xyz`` expands automatically to ``-x -y -z``, so (for example) ``mlr cut -of shape,flag`` is the same as ``mlr cut -o -f shape,flag``.
|
||||
* ``--foo=bar`` expands automatically to ``--foo bar``, so (for example) ``mlr --ifs=comma`` is the same as ``mlr --ifs comma``.
|
||||
* ``--mfrom``, ``--load``, ``--mload`` as described at [TODO:linkify].
|
||||
* `-xyz` expands automatically to `-x -y -z`, so (for example) `mlr cut -of shape,flag` is the same as `mlr cut -o -f shape,flag`.
|
||||
* `--foo=bar` expands automatically to `--foo bar`, so (for example) `mlr --ifs=comma` is the same as `mlr --ifs comma`.
|
||||
* `--mfrom`, `--load`, `--mload` as described at [TODO:linkify].
|
||||
|
||||
## Improved error messages for DSL parsing
|
||||
|
||||
For ``mlr put`` and ``mlr filter``, parse-error messages now include location information:
|
||||
For `mlr put` and `mlr filter`, parse-error messages now include location information:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
mlr: cannot parse DSL expression.
|
||||
Parse error on token ">" at line 63 columnn 7.
|
||||
</pre>
|
||||
|
||||
## Developer-specific aspects
|
||||
|
||||
* Miller has been ported from C to Go. Developer notes: https://github.com/johnkerl/miller/blob/main/go/README.md
|
||||
* Completely reworked regression testing, including running on Windows
|
||||
* Miller has been ported from C to Go. Developer notes: [https://github.com/johnkerl/miller/blob/main/go/README.md](https://github.com/johnkerl/miller/blob/main/go/README.md).
|
||||
* Completely reworked regression testing, including regresstion-testing now running on Windows.
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ See also the [list of issues tagged with go-port](https://github.com/johnkerl/mi
|
|||
|
||||
## Documentation improvements
|
||||
|
||||
Documentation (what you're reading here) and on-line help (``mlr --help``) have been completely reworked.
|
||||
Documentation (what you're reading here) and on-line help (`mlr --help`) have been completely reworked.
|
||||
|
||||
In the initial release, the focus was convincing users already familiar with
|
||||
``awk``/``grep``/``cut`` that Miller was a viable option. Over time it's become
|
||||
`awk`/`grep`/`cut` that Miller was a viable option. Over time it's become
|
||||
clear that many users aren't expert with these. The focus has shifted toward a
|
||||
higher quantity of more introductory/accessible material for command-line data
|
||||
processing.
|
||||
|
|
@ -24,14 +24,14 @@ now discussed first, and more examples use CSV.
|
|||
|
||||
## JSON support, and arrays
|
||||
|
||||
Arrays are now supported in Miller's ``put``/``filter`` programming language,
|
||||
as described at :doc:`reference-dsl-arrays`. Also, ``array`` is now a keyword
|
||||
Arrays are now supported in Miller's `put`/`filter` programming language,
|
||||
as described at [Reference: arrays](reference-dsl-arrays.md). Also, `array` is now a keyword
|
||||
so this is no longer usable as a local-variable or UDF name.
|
||||
|
||||
JSON support is improved:
|
||||
|
||||
* Direct support for arrays means that you can now use Miller to process more JSON files.
|
||||
* Streamable JSON parsing: Miller's internal record-processing pipeline starts as soon as the first record is read (which was already the case for other file formats). This means that, unless records are wrapped with outermost ``[...]``, Miller now handles JSON in ``tail -f`` contexts like it does for other file formats.
|
||||
* Streamable JSON parsing: Miller's internal record-processing pipeline starts as soon as the first record is read (which was already the case for other file formats). This means that, unless records are wrapped with outermost `[...]`, Miller now handles JSON in `tail -f` contexts like it does for other file formats.
|
||||
* Flatten/unflatten -- TODO pick a name and link to a separate page/section
|
||||
|
||||
## Improved Windows experience
|
||||
|
|
@ -43,11 +43,7 @@ Binaries are reliably available using GitHub Actions: see also [Installation](in
|
|||
|
||||
## In-process support for compressed input
|
||||
|
||||
In addition to ``--prepipe gunzip``, you can now use the ``--gzin`` flag. In
|
||||
fact, if your files end in ``.gz`` you don't even need to do that -- Miller
|
||||
will autodetect by file extension and automatically uncompress ``mlr --csv cat
|
||||
foo.csv.gz``. Similarly for ``.z`` and ``.bz2`` files. Please see section
|
||||
[TODO:linkify] for more information.
|
||||
In addition to `--prepipe gunzip`, you can now use the `--gzin` flag. In fact, if your files end in `.gz` you don't even need to do that -- Miller will autodetect by file extension and automatically uncompress `mlr --csv cat foo.csv.gz`. Similarly for `.z` and `.bz2` files. Please see section [TODO:linkify] for more information.
|
||||
|
||||
## Output colorization
|
||||
|
||||
|
|
@ -59,12 +55,12 @@ The most central part of Miller 6 is a deep refactor of how data values are pars
|
|||
from file contents, how types are inferred, and how they're converted back to
|
||||
text into output files.
|
||||
|
||||
This was all initiated by https://github.com/johnkerl/miller/issues/151.
|
||||
This was all initiated by [https://github.com/johnkerl/miller/issues/151](https://github.com/johnkerl/miller/issues/151).
|
||||
|
||||
In Miller 5 and below, all values were stored as strings, then only converted
|
||||
to int/float as-needed, for example when a particular field was referenced in
|
||||
the ``stats1`` or ``put`` verbs. This led to awkwardnesses such as the ``-S``
|
||||
and ``-F`` flags for ``put`` and ``filter``.
|
||||
the `stats1` or `put` verbs. This led to awkwardnesses such as the `-S`
|
||||
and `-F` flags for `put` and `filter`.
|
||||
|
||||
In Miller 6, things parseable as int/float are treated as such from the moment
|
||||
the input data is read, and these are passed along through the verb chain. All
|
||||
|
|
@ -74,7 +70,7 @@ a numeric field isn't modified during the processing chain, it's printed out
|
|||
the way it arrived. Also, quoted values in JSON strings are flagged as being
|
||||
strings throughout the processing chain.
|
||||
|
||||
For example (see https://github.com/johnkerl/miller/issues/178) you can now do
|
||||
For example (see [https://github.com/johnkerl/miller/issues/178](https://github.com/johnkerl/miller/issues/178)) you can now do
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
echo '{ "a": "0123" }' | mlr --json cat
|
||||
|
|
@ -90,21 +86,23 @@ Miller now has a read-evaluate-print-loop ([REPL](repl.md)) where you can single
|
|||
|
||||
## New DSL functions / operators
|
||||
|
||||
* String-hashing functions :ref:`reference-dsl-md5`, :ref:`reference-dsl-sha1`, :ref:`reference-dsl-sha256`, and :ref:`reference-dsl-sha512`.
|
||||
* Platform-property functions :ref:`reference-dsl-hostname`, :ref:`reference-dsl-os`, and :ref:`reference-dsl-version`.
|
||||
* Unsigned right-shift :ref:`reference-dsl-ursh` along with ``>>>=``.
|
||||
* String-hashing functions [md5](reference-dsl-builtin-functions.md#md5), [sha1](reference-dsl-builtin-functions.md#sha1), [sha256](reference-dsl-builtin-functions.md#sha256), and [sha512](reference-dsl-builtin-functions.md#sha512).
|
||||
|
||||
* Platform-property functions [hostname](reference-dsl-builtin-functions.md#hostname), [os](reference-dsl-builtin-functions.md#os), and [version](reference-dsl-builtin-functions.md#version).
|
||||
|
||||
* Unsigned right-shift [`>>>`](reference-dsl-builtin-functions.md#ursh) along with `>>>=`.
|
||||
|
||||
## Improved command-line parsing
|
||||
|
||||
Miller 6 has getoptish command-line parsing (https://github.com/johnkerl/miller/pull/467):
|
||||
|
||||
* ``-xyz`` expands automatically to ``-x -y -z``, so (for example) ``mlr cut -of shape,flag`` is the same as ``mlr cut -o -f shape,flag``.
|
||||
* ``--foo=bar`` expands automatically to ``--foo bar``, so (for example) ``mlr --ifs=comma`` is the same as ``mlr --ifs comma``.
|
||||
* ``--mfrom``, ``--load``, ``--mload`` as described at [TODO:linkify].
|
||||
* `-xyz` expands automatically to `-x -y -z`, so (for example) `mlr cut -of shape,flag` is the same as `mlr cut -o -f shape,flag`.
|
||||
* `--foo=bar` expands automatically to `--foo bar`, so (for example) `mlr --ifs=comma` is the same as `mlr --ifs comma`.
|
||||
* `--mfrom`, `--load`, `--mload` as described at [TODO:linkify].
|
||||
|
||||
## Improved error messages for DSL parsing
|
||||
|
||||
For ``mlr put`` and ``mlr filter``, parse-error messages now include location information:
|
||||
For `mlr put` and `mlr filter`, parse-error messages now include location information:
|
||||
|
||||
GENMD_CARDIFY
|
||||
mlr: cannot parse DSL expression.
|
||||
|
|
@ -113,5 +111,5 @@ GENMD_EOF
|
|||
|
||||
## Developer-specific aspects
|
||||
|
||||
* Miller has been ported from C to Go. Developer notes: https://github.com/johnkerl/miller/blob/main/go/README.md
|
||||
* Completely reworked regression testing, including running on Windows
|
||||
* Miller has been ported from C to Go. Developer notes: [https://github.com/johnkerl/miller/blob/main/go/README.md](https://github.com/johnkerl/miller/blob/main/go/README.md).
|
||||
* Completely reworked regression testing, including regresstion-testing now running on Windows.
|
||||
|
|
|
|||
|
|
@ -5,26 +5,32 @@
|
|||
|
||||
Suppose you want to replace spaces with underscores in your column names:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/spaces.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a b c,def,g h i
|
||||
123,4567,890
|
||||
2468,1357,3579
|
||||
9987,3312,4543
|
||||
</pre>
|
||||
|
||||
The simplest way is to use ``mlr rename`` with ``-g`` (for global replace, not just first occurrence of space within each field) and ``-r`` for pattern-matching (rather than explicit single-column renames):
|
||||
The simplest way is to use `mlr rename` with `-g` (for global replace, not just first occurrence of space within each field) and `-r` for pattern-matching (rather than explicit single-column renames):
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --csv rename -g -r ' ,_' data/spaces.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a_b_c,def,g_h_i
|
||||
123,4567,890
|
||||
2468,1357,3579
|
||||
9987,3312,4543
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --csv --opprint rename -g -r ' ,_' data/spaces.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a_b_c def g_h_i
|
||||
123 4567 890
|
||||
2468 1357 3579
|
||||
|
|
@ -33,8 +39,10 @@ a_b_c def g_h_i
|
|||
|
||||
You can also do this with a for-loop:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/bulk-rename-for-loop.mlr</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
map newrec = {};
|
||||
for (oldk, v in $*) {
|
||||
newrec[gsub(oldk, " ", "_")] = v;
|
||||
|
|
@ -42,8 +50,10 @@ for (oldk, v in $*) {
|
|||
$* = newrec
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsv --opprint put -f data/bulk-rename-for-loop.mlr data/spaces.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a_b_c def g_h_i
|
||||
123 4567 890
|
||||
2468 1357 3579
|
||||
|
|
@ -52,24 +62,30 @@ a_b_c def g_h_i
|
|||
|
||||
## Search-and-replace over all fields
|
||||
|
||||
How to do ``$name = gsub($name, "old", "new")`` for all fields?
|
||||
How to do `$name = gsub($name, "old", "new")` for all fields?
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/sar.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a,b,c
|
||||
the quick,brown fox,jumped
|
||||
over,the,lazy dogs
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/sar.mlr</b>
|
||||
for (k in $*) {
|
||||
$[k] = gsub($[k], "e", "X");
|
||||
}
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
for (k in $*) {
|
||||
$[k] = gsub($[k], "e", "X");
|
||||
}
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --csv put -f data/sar.mlr data/sar.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a,b,c
|
||||
thX quick,brown fox,jumpXd
|
||||
ovXr,thX,lazy dogs
|
||||
|
|
@ -77,10 +93,12 @@ ovXr,thX,lazy dogs
|
|||
|
||||
## Full field renames and reassigns
|
||||
|
||||
Using Miller 5.0.0's map literals and assigning to ``$*``, you can fully generalize :ref:`mlr rename <reference-verbs-rename>`, :ref:`mlr reorder <reference-verbs-reorder>`, etc.
|
||||
Using Miller 5.0.0's map literals and assigning to `$*`, you can fully generalize [rename](reference-verbs.md#rename), [reorder](reference-verbs.md#reorder), etc.
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,b=pan,i=1,x=0.3467901443380824,y=0.7268028627434533
|
||||
a=eks,b=pan,i=2,x=0.7586799647899636,y=0.5221511083334797
|
||||
a=wye,b=wye,i=3,x=0.20460330576630303,y=0.33831852551664776
|
||||
|
|
@ -88,7 +106,7 @@ a=eks,b=wye,i=4,x=0.38139939387114097,y=0.13418874328430463
|
|||
a=wye,b=pan,i=5,x=0.5732889198020006,y=0.8636244699032729
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put '</b>
|
||||
<b> begin {</b>
|
||||
<b> @i_cumu = 0;</b>
|
||||
|
|
@ -104,6 +122,8 @@ a=wye,b=pan,i=5,x=0.5732889198020006,y=0.8636244699032729
|
|||
<b> "x": $y,</b>
|
||||
<b> };</b>
|
||||
<b>' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
z=0.3467901443380824,KEYFIELD=pan,i=1,b=pan,y=0.3467901443380824,x=0.7268028627434533
|
||||
z=0.7586799647899636,KEYFIELD=eks,i=3,b=pan,y=0.7586799647899636,x=0.5221511083334797
|
||||
z=0.20460330576630303,KEYFIELD=wye,i=6,b=wye,y=0.20460330576630303,x=0.33831852551664776
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ GENMD_RUN_COMMAND
|
|||
cat data/spaces.csv
|
||||
GENMD_EOF
|
||||
|
||||
The simplest way is to use ``mlr rename`` with ``-g`` (for global replace, not just first occurrence of space within each field) and ``-r`` for pattern-matching (rather than explicit single-column renames):
|
||||
The simplest way is to use `mlr rename` with `-g` (for global replace, not just first occurrence of space within each field) and `-r` for pattern-matching (rather than explicit single-column renames):
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --csv rename -g -r ' ,_' data/spaces.csv
|
||||
|
|
@ -30,7 +30,7 @@ GENMD_EOF
|
|||
|
||||
## Search-and-replace over all fields
|
||||
|
||||
How to do ``$name = gsub($name, "old", "new")`` for all fields?
|
||||
How to do `$name = gsub($name, "old", "new")` for all fields?
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
cat data/sar.csv
|
||||
|
|
@ -46,7 +46,7 @@ GENMD_EOF
|
|||
|
||||
## Full field renames and reassigns
|
||||
|
||||
Using Miller 5.0.0's map literals and assigning to ``$*``, you can fully generalize :ref:`mlr rename <reference-verbs-rename>`, :ref:`mlr reorder <reference-verbs-reorder>`, etc.
|
||||
Using Miller 5.0.0's map literals and assigning to `$*`, you can fully generalize [rename](reference-verbs.md#rename), [reorder](reference-verbs.md#reorder), etc.
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
cat data/small
|
||||
|
|
|
|||
|
|
@ -1,40 +1,40 @@
|
|||
<!--- PLEASE DO NOT EDIT DIRECTLY. EDIT THE .md.in FILE PLEASE. --->
|
||||
# How original is Miller?
|
||||
|
||||
It isn't. Miller is one of many, many participants in the online-analytical-processing culture. Other key participants include ``awk``, SQL, spreadsheets, etc. etc. etc. Far from being an original concept, Miller explicitly strives to imitate several existing tools:
|
||||
It isn't. Miller is one of many, many participants in the online-analytical-processing culture. Other key participants include `awk`, SQL, spreadsheets, etc. etc. etc. Far from being an original concept, Miller explicitly strives to imitate several existing tools:
|
||||
|
||||
**The Unix toolkit**: Intentional similarities as described in [Unix-toolkit Context](feature-comparison.md).
|
||||
|
||||
Recipes abound for command-line data analysis using the Unix toolkit. Here are just a couple of my favorites:
|
||||
|
||||
* http://en.wikibooks.org/wiki/Ad_Hoc_Data_Analysis_From_The_Unix_Command_Line
|
||||
* http://www.gregreda.com/2013/07/15/unix-commands-for-data-science
|
||||
* https://github.com/dbohdan/structured-text-tools
|
||||
* [http://en.wikibooks.org/wiki/Ad_Hoc_Data_Analysis_From_The_Unix_Command_Line](http://en.wikibooks.org/wiki/Ad_Hoc_Data_Analysis_From_The_Unix_Command_Line)
|
||||
* [http://www.gregreda.com/2013/07/15/unix-commands-for-data-science](http://www.gregreda.com/2013/07/15/unix-commands-for-data-science)
|
||||
* [https://github.com/dbohdan/structured-text-tools](https://github.com/dbohdan/structured-text-tools)
|
||||
|
||||
**RecordStream**: Miller owes particular inspiration to [RecordStream](https://github.com/benbernard/RecordStream). The key difference is that RecordStream is a Perl-based tool for manipulating JSON (including requiring it to separately manipulate other formats such as CSV into and out of JSON), while Miller is fast Go which handles its formats natively. The similarities include the ``sort``, ``stats1`` (analog of RecordStream's ``collate``), and ``delta`` operations, as well as ``filter`` and ``put``, and pretty-print formatting.
|
||||
**RecordStream**: Miller owes particular inspiration to [RecordStream](https://github.com/benbernard/RecordStream). The key difference is that RecordStream is a Perl-based tool for manipulating JSON (including requiring it to separately manipulate other formats such as CSV into and out of JSON), while Miller is fast Go which handles its formats natively. The similarities include the `sort`, `stats1` (analog of RecordStream's `collate`), and `delta` operations, as well as `filter` and `put`, and pretty-print formatting.
|
||||
|
||||
**stats_m**: A third source of lineage is my Python [stats_m](https://github.com/johnkerl/scripts-math/tree/master/stats) module. This includes simple single-pass algorithms which form Miller's ``stats1`` and ``stats2`` subcommands.
|
||||
**stats_m**: A third source of lineage is my Python [stats_m](https://github.com/johnkerl/scripts-math/tree/master/stats) module. This includes simple single-pass algorithms which form Miller's `stats1` and `stats2` subcommands.
|
||||
|
||||
**SQL**: Fourthly, Miller's ``group-by`` command name is from SQL, as is the term ``aggregate``.
|
||||
**SQL**: Fourthly, Miller's `group-by` command name is from SQL, as is the term `aggregate`.
|
||||
|
||||
**Added value**: Miller's added values include:
|
||||
|
||||
* Name-indexing, compared to the Unix toolkit's positional indexing.
|
||||
* Raw speed, compared to ``awk``, RecordStream, ``stats_m``, or various other kinds of Python/Ruby/etc. scripts one can easily create.
|
||||
* Raw speed, compared to `awk`, RecordStream, `stats_m`, or various other kinds of Python/Ruby/etc. scripts one can easily create.
|
||||
* Compact keystroking for many common tasks, with a decent amount of flexibility.
|
||||
* Ability to handle text files on the Unix pipe, without need for creating database tables, compared to SQL databases.
|
||||
* Various file formats, and on-the-fly format conversion.
|
||||
|
||||
**jq**: Miller does for name-indexed text what [jq](https://stedolan.github.io/jq/) does for JSON. If you're not already familiar with ``jq``, please check it out!.
|
||||
**jq**: Miller does for name-indexed text what [jq](https://stedolan.github.io/jq/) does for JSON. If you're not already familiar with `jq`, please check it out!.
|
||||
|
||||
**What about similar tools?**
|
||||
|
||||
Here's a comprehensive list: https://github.com/dbohdan/structured-text-tools. Last I knew it doesn't mention [rows](https://github.com/turicas/rows) so here's a plug for that as well. As it turns out, I learned about most of these after writing Miller.
|
||||
Here's a comprehensive list: [https://github.com/dbohdan/structured-text-tools](https://github.com/dbohdan/structured-text-tools). Last I knew it doesn't mention [rows](https://github.com/turicas/rows) so here's a plug for that as well. As it turns out, I learned about most of these after writing Miller.
|
||||
|
||||
**What about DOTADIW?** One of the key points of the [Unix philosophy](http://en.wikipedia.org/wiki/Unix_philosophy) is that a tool should do one thing and do it well. Hence ``sort`` and ``cut`` do just one thing. Why does Miller put ``awk``-like processing, a few SQL-like operations, and statistical reduction all into one tool? This is a fair question. First note that many standard tools, such as ``awk`` and ``perl``, do quite a few things -- as does ``jq``. But I could have pushed for putting format awareness and name-indexing options into ``cut``, ``awk``, and so on (so you could do ``cut -f hostname,uptime`` or ``awk '{sum += $x*$y}END{print sum}'``). Patching ``cut``, ``sort``, etc. on multiple operating systems is a non-starter in terms of uptake. Moreover, it makes sense for me to have Miller be a tool which collects together format-aware record-stream processing into one place, with good reuse of Miller-internal library code for its various features.
|
||||
**What about DOTADIW?** One of the key points of the [Unix philosophy](http://en.wikipedia.org/wiki/Unix_philosophy) is that a tool should do one thing and do it well. Hence `sort` and `cut` do just one thing. Why does Miller put `awk`-like processing, a few SQL-like operations, and statistical reduction all into one tool? This is a fair question. First note that many standard tools, such as `awk` and `perl`, do quite a few things -- as does `jq`. But I could have pushed for putting format awareness and name-indexing options into `cut`, `awk`, and so on (so you could do `cut -f hostname,uptime` or `awk '{sum += $x*$y}END{print sum}'`). Patching `cut`, `sort`, etc. on multiple operating systems is a non-starter in terms of uptake. Moreover, it makes sense for me to have Miller be a tool which collects together format-aware record-stream processing into one place, with good reuse of Miller-internal library code for its various features.
|
||||
|
||||
**Why not use Perl/Python/Ruby etc.?** Maybe you should. With those tools you'll get far more expressive power, and sufficiently quick turnaround time for small-to-medium-sized data. Using Miller you'll get something less than a complete programming language, but which is fast, with moderate amounts of flexibility and much less keystroking.
|
||||
|
||||
When I was first developing Miller I made a survey of several languages. Using low-level implementation languages like C, Go, Rust, and Nim, I'd need to create my own domain-specific language (DSL) which would always be less featured than a full programming language, but I'd get better performance. Using high-level interpreted languages such as Perl/Python/Ruby I'd get the language's ``eval`` for free and I wouldn't need a DSL; Miller would have mainly been a set of format-specific I/O hooks. If I'd gotten good enough performance from the latter I'd have done it without question and Miller would be far more flexible. But high-level languages win the performance criteria by a landslide so we have Miller in Go with a custom DSL.
|
||||
When I was first developing Miller I made a survey of several languages. Using low-level implementation languages like C, Go, Rust, and Nim, I'd need to create my own domain-specific language (DSL) which would always be less featured than a full programming language, but I'd get better performance. Using high-level interpreted languages such as Perl/Python/Ruby I'd get the language's `eval` for free and I wouldn't need a DSL; Miller would have mainly been a set of format-specific I/O hooks. If I'd gotten good enough performance from the latter I'd have done it without question and Miller would be far more flexible. But high-level languages win the performance criteria by a landslide so we have Miller in Go with a custom DSL.
|
||||
|
||||
**No, really, why one more command-line data-manipulation tool?** I wrote Miller because I was frustrated with tools like ``grep``, ``sed``, and so on being *line-aware* without being *format-aware*. The single most poignant example I can think of is seeing people grep data lines out of their CSV files and sadly losing their header lines. While some lighter-than-SQL processing is very nice to have, at core I wanted the format-awareness of [RecordStream](https://github.com/benbernard/RecordStream) combined with the raw speed of the Unix toolkit. Miller does precisely that.
|
||||
**No, really, why one more command-line data-manipulation tool?** I wrote Miller because I was frustrated with tools like `grep`, `sed`, and so on being *line-aware* without being *format-aware*. The single most poignant example I can think of is seeing people grep data lines out of their CSV files and sadly losing their header lines. While some lighter-than-SQL processing is very nice to have, at core I wanted the format-awareness of [RecordStream](https://github.com/benbernard/RecordStream) combined with the raw speed of the Unix toolkit. Miller does precisely that.
|
||||
|
|
|
|||
|
|
@ -1,39 +1,39 @@
|
|||
# How original is Miller?
|
||||
|
||||
It isn't. Miller is one of many, many participants in the online-analytical-processing culture. Other key participants include ``awk``, SQL, spreadsheets, etc. etc. etc. Far from being an original concept, Miller explicitly strives to imitate several existing tools:
|
||||
It isn't. Miller is one of many, many participants in the online-analytical-processing culture. Other key participants include `awk`, SQL, spreadsheets, etc. etc. etc. Far from being an original concept, Miller explicitly strives to imitate several existing tools:
|
||||
|
||||
**The Unix toolkit**: Intentional similarities as described in [Unix-toolkit Context](feature-comparison.md).
|
||||
|
||||
Recipes abound for command-line data analysis using the Unix toolkit. Here are just a couple of my favorites:
|
||||
|
||||
* http://en.wikibooks.org/wiki/Ad_Hoc_Data_Analysis_From_The_Unix_Command_Line
|
||||
* http://www.gregreda.com/2013/07/15/unix-commands-for-data-science
|
||||
* https://github.com/dbohdan/structured-text-tools
|
||||
* [http://en.wikibooks.org/wiki/Ad_Hoc_Data_Analysis_From_The_Unix_Command_Line](http://en.wikibooks.org/wiki/Ad_Hoc_Data_Analysis_From_The_Unix_Command_Line)
|
||||
* [http://www.gregreda.com/2013/07/15/unix-commands-for-data-science](http://www.gregreda.com/2013/07/15/unix-commands-for-data-science)
|
||||
* [https://github.com/dbohdan/structured-text-tools](https://github.com/dbohdan/structured-text-tools)
|
||||
|
||||
**RecordStream**: Miller owes particular inspiration to [RecordStream](https://github.com/benbernard/RecordStream). The key difference is that RecordStream is a Perl-based tool for manipulating JSON (including requiring it to separately manipulate other formats such as CSV into and out of JSON), while Miller is fast Go which handles its formats natively. The similarities include the ``sort``, ``stats1`` (analog of RecordStream's ``collate``), and ``delta`` operations, as well as ``filter`` and ``put``, and pretty-print formatting.
|
||||
**RecordStream**: Miller owes particular inspiration to [RecordStream](https://github.com/benbernard/RecordStream). The key difference is that RecordStream is a Perl-based tool for manipulating JSON (including requiring it to separately manipulate other formats such as CSV into and out of JSON), while Miller is fast Go which handles its formats natively. The similarities include the `sort`, `stats1` (analog of RecordStream's `collate`), and `delta` operations, as well as `filter` and `put`, and pretty-print formatting.
|
||||
|
||||
**stats_m**: A third source of lineage is my Python [stats_m](https://github.com/johnkerl/scripts-math/tree/master/stats) module. This includes simple single-pass algorithms which form Miller's ``stats1`` and ``stats2`` subcommands.
|
||||
**stats_m**: A third source of lineage is my Python [stats_m](https://github.com/johnkerl/scripts-math/tree/master/stats) module. This includes simple single-pass algorithms which form Miller's `stats1` and `stats2` subcommands.
|
||||
|
||||
**SQL**: Fourthly, Miller's ``group-by`` command name is from SQL, as is the term ``aggregate``.
|
||||
**SQL**: Fourthly, Miller's `group-by` command name is from SQL, as is the term `aggregate`.
|
||||
|
||||
**Added value**: Miller's added values include:
|
||||
|
||||
* Name-indexing, compared to the Unix toolkit's positional indexing.
|
||||
* Raw speed, compared to ``awk``, RecordStream, ``stats_m``, or various other kinds of Python/Ruby/etc. scripts one can easily create.
|
||||
* Raw speed, compared to `awk`, RecordStream, `stats_m`, or various other kinds of Python/Ruby/etc. scripts one can easily create.
|
||||
* Compact keystroking for many common tasks, with a decent amount of flexibility.
|
||||
* Ability to handle text files on the Unix pipe, without need for creating database tables, compared to SQL databases.
|
||||
* Various file formats, and on-the-fly format conversion.
|
||||
|
||||
**jq**: Miller does for name-indexed text what [jq](https://stedolan.github.io/jq/) does for JSON. If you're not already familiar with ``jq``, please check it out!.
|
||||
**jq**: Miller does for name-indexed text what [jq](https://stedolan.github.io/jq/) does for JSON. If you're not already familiar with `jq`, please check it out!.
|
||||
|
||||
**What about similar tools?**
|
||||
|
||||
Here's a comprehensive list: https://github.com/dbohdan/structured-text-tools. Last I knew it doesn't mention [rows](https://github.com/turicas/rows) so here's a plug for that as well. As it turns out, I learned about most of these after writing Miller.
|
||||
Here's a comprehensive list: [https://github.com/dbohdan/structured-text-tools](https://github.com/dbohdan/structured-text-tools). Last I knew it doesn't mention [rows](https://github.com/turicas/rows) so here's a plug for that as well. As it turns out, I learned about most of these after writing Miller.
|
||||
|
||||
**What about DOTADIW?** One of the key points of the [Unix philosophy](http://en.wikipedia.org/wiki/Unix_philosophy) is that a tool should do one thing and do it well. Hence ``sort`` and ``cut`` do just one thing. Why does Miller put ``awk``-like processing, a few SQL-like operations, and statistical reduction all into one tool? This is a fair question. First note that many standard tools, such as ``awk`` and ``perl``, do quite a few things -- as does ``jq``. But I could have pushed for putting format awareness and name-indexing options into ``cut``, ``awk``, and so on (so you could do ``cut -f hostname,uptime`` or ``awk '{sum += $x*$y}END{print sum}'``). Patching ``cut``, ``sort``, etc. on multiple operating systems is a non-starter in terms of uptake. Moreover, it makes sense for me to have Miller be a tool which collects together format-aware record-stream processing into one place, with good reuse of Miller-internal library code for its various features.
|
||||
**What about DOTADIW?** One of the key points of the [Unix philosophy](http://en.wikipedia.org/wiki/Unix_philosophy) is that a tool should do one thing and do it well. Hence `sort` and `cut` do just one thing. Why does Miller put `awk`-like processing, a few SQL-like operations, and statistical reduction all into one tool? This is a fair question. First note that many standard tools, such as `awk` and `perl`, do quite a few things -- as does `jq`. But I could have pushed for putting format awareness and name-indexing options into `cut`, `awk`, and so on (so you could do `cut -f hostname,uptime` or `awk '{sum += $x*$y}END{print sum}'`). Patching `cut`, `sort`, etc. on multiple operating systems is a non-starter in terms of uptake. Moreover, it makes sense for me to have Miller be a tool which collects together format-aware record-stream processing into one place, with good reuse of Miller-internal library code for its various features.
|
||||
|
||||
**Why not use Perl/Python/Ruby etc.?** Maybe you should. With those tools you'll get far more expressive power, and sufficiently quick turnaround time for small-to-medium-sized data. Using Miller you'll get something less than a complete programming language, but which is fast, with moderate amounts of flexibility and much less keystroking.
|
||||
|
||||
When I was first developing Miller I made a survey of several languages. Using low-level implementation languages like C, Go, Rust, and Nim, I'd need to create my own domain-specific language (DSL) which would always be less featured than a full programming language, but I'd get better performance. Using high-level interpreted languages such as Perl/Python/Ruby I'd get the language's ``eval`` for free and I wouldn't need a DSL; Miller would have mainly been a set of format-specific I/O hooks. If I'd gotten good enough performance from the latter I'd have done it without question and Miller would be far more flexible. But high-level languages win the performance criteria by a landslide so we have Miller in Go with a custom DSL.
|
||||
When I was first developing Miller I made a survey of several languages. Using low-level implementation languages like C, Go, Rust, and Nim, I'd need to create my own domain-specific language (DSL) which would always be less featured than a full programming language, but I'd get better performance. Using high-level interpreted languages such as Perl/Python/Ruby I'd get the language's `eval` for free and I wouldn't need a DSL; Miller would have mainly been a set of format-specific I/O hooks. If I'd gotten good enough performance from the latter I'd have done it without question and Miller would be far more flexible. But high-level languages win the performance criteria by a landslide so we have Miller in Go with a custom DSL.
|
||||
|
||||
**No, really, why one more command-line data-manipulation tool?** I wrote Miller because I was frustrated with tools like ``grep``, ``sed``, and so on being *line-aware* without being *format-aware*. The single most poignant example I can think of is seeing people grep data lines out of their CSV files and sadly losing their header lines. While some lighter-than-SQL processing is very nice to have, at core I wanted the format-awareness of [RecordStream](https://github.com/benbernard/RecordStream) combined with the raw speed of the Unix toolkit. Miller does precisely that.
|
||||
**No, really, why one more command-line data-manipulation tool?** I wrote Miller because I was frustrated with tools like `grep`, `sed`, and so on being *line-aware* without being *format-aware*. The single most poignant example I can think of is seeing people grep data lines out of their CSV files and sadly losing their header lines. While some lighter-than-SQL processing is very nice to have, at core I wanted the format-awareness of [RecordStream](https://github.com/benbernard/RecordStream) combined with the raw speed of the Unix toolkit. Miller does precisely that.
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
As of version 6.0.0, Miller supports output-colorization. Here are examples using side-by-side black-background and white-background terminals:
|
||||
|
||||
.. image:: pix/colorization.png
|
||||

|
||||
|
||||
Things having colors:
|
||||
|
||||
|
|
@ -16,38 +16,37 @@ Rules for colorization:
|
|||
|
||||
* By default, colorize output only if writing to stdout and stdout is a TTY.
|
||||
|
||||
* Example: color: ``mlr --csv cat foo.csv``
|
||||
* Example: no color: ``mlr --csv cat foo.csv > bar.csv``
|
||||
* Example: no color: ``mlr --csv cat foo.csv | less``
|
||||
* Example: color: `mlr --csv cat foo.csv`
|
||||
* Example: no color: `mlr --csv cat foo.csv > bar.csv`
|
||||
* Example: no color: `mlr --csv cat foo.csv | less`
|
||||
|
||||
* The default colors were chosen since they look OK with white or black terminal background, and are differentiable with common varieties of human color vision.
|
||||
|
||||
Mechanisms for colorization:
|
||||
|
||||
* Miller uses ANSI escape sequences only. This does not work on Windows except on Cygwin.
|
||||
* Requires ``TERM`` environment variable to be set to non-empty string.
|
||||
* Requires `TERM` environment variable to be set to non-empty string.
|
||||
* Doesn't try to check to see whether the terminal is capable of 256-color ANSI vs 16-color ANSI. Note that if colors are in the range 0..15 then 16-color ANSI escapes are used, so this is in the user's control.
|
||||
|
||||
How you can control colorization:
|
||||
|
||||
* Suppression/unsuppression:
|
||||
|
||||
* ``export MLR_NO_COLOR=true`` means Miller won't color even when it normally would.
|
||||
* ``export MLR_ALWAYS_COLOR=true`` means Miller will color even when it normally would not. For example, you might want to use this when piping ``mlr`` output to ``less -r``.
|
||||
* Command-line flags ``--no-color`` or ``-M``, ``--always-color`` or ``-C``.
|
||||
* `export MLR_NO_COLOR=true` means Miller won't color even when it normally would.
|
||||
* `export MLR_ALWAYS_COLOR=true` means Miller will color even when it normally would not. For example, you might want to use this when piping `mlr` output to `less -r`.
|
||||
* Command-line flags `--no-color` or `-M`, `--always-color` or `-C`.
|
||||
|
||||
|
||||
* Color choices can be specified by using environment variables or command-line flags, with values 0..255:
|
||||
|
||||
* ``export MLR_KEY_COLOR=208``
|
||||
* ``export MLR_VALUE_COLOR=33``
|
||||
* Likewise for ``MLR_PASS_COLOR``, ``MLR_FAIL_COLOR``, ``MLR_HELP_COLOR``, ``MLR_REPL_PS1_COLOR``, and ``MLR_REPL_PS2_COLOR``.
|
||||
* Command-line flags ``--key-color 208``, ``--value-color 33``, etc., and likewise for ``--pass-color``, ``--fail-color``, ``--repl-ps1-color``, ``--repl-ps2-color``, and ``--help-color``.
|
||||
* `export MLR_KEY_COLOR=208`
|
||||
* `export MLR_VALUE_COLOR=33`
|
||||
* Likewise for `MLR_PASS_COLOR`, `MLR_FAIL_COLOR`, `MLR_HELP_COLOR`, `MLR_REPL_PS1_COLOR`, and `MLR_REPL_PS2_COLOR`.
|
||||
* Command-line flags `--key-color 208`, `--value-color 33`, etc., and likewise for `--pass-color`, `--fail-color`, `--repl-ps1-color`, `--repl-ps2-color`, and `--help-color`.
|
||||
* This is particularly useful if your terminal's background color clashes with current settings.
|
||||
|
||||
If environment-variable settings and command-line flags are both provided, the latter take precedence.
|
||||
|
||||
Please do ``mlr --list-color-codes`` to see the available color codes (like ``170``), and ``mlr --list-color-names`` to see available names (like ``orchid``).
|
||||
|
||||
.. image:: pix/colorization2.png
|
||||
Please do `mlr --list-color-codes` to see the available color codes (like `170`), and `mlr --list-color-names` to see available names (like `orchid`).
|
||||
|
||||

|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
As of version 6.0.0, Miller supports output-colorization. Here are examples using side-by-side black-background and white-background terminals:
|
||||
|
||||
.. image:: pix/colorization.png
|
||||

|
||||
|
||||
Things having colors:
|
||||
|
||||
|
|
@ -15,38 +15,37 @@ Rules for colorization:
|
|||
|
||||
* By default, colorize output only if writing to stdout and stdout is a TTY.
|
||||
|
||||
* Example: color: ``mlr --csv cat foo.csv``
|
||||
* Example: no color: ``mlr --csv cat foo.csv > bar.csv``
|
||||
* Example: no color: ``mlr --csv cat foo.csv | less``
|
||||
* Example: color: `mlr --csv cat foo.csv`
|
||||
* Example: no color: `mlr --csv cat foo.csv > bar.csv`
|
||||
* Example: no color: `mlr --csv cat foo.csv | less`
|
||||
|
||||
* The default colors were chosen since they look OK with white or black terminal background, and are differentiable with common varieties of human color vision.
|
||||
|
||||
Mechanisms for colorization:
|
||||
|
||||
* Miller uses ANSI escape sequences only. This does not work on Windows except on Cygwin.
|
||||
* Requires ``TERM`` environment variable to be set to non-empty string.
|
||||
* Requires `TERM` environment variable to be set to non-empty string.
|
||||
* Doesn't try to check to see whether the terminal is capable of 256-color ANSI vs 16-color ANSI. Note that if colors are in the range 0..15 then 16-color ANSI escapes are used, so this is in the user's control.
|
||||
|
||||
How you can control colorization:
|
||||
|
||||
* Suppression/unsuppression:
|
||||
|
||||
* ``export MLR_NO_COLOR=true`` means Miller won't color even when it normally would.
|
||||
* ``export MLR_ALWAYS_COLOR=true`` means Miller will color even when it normally would not. For example, you might want to use this when piping ``mlr`` output to ``less -r``.
|
||||
* Command-line flags ``--no-color`` or ``-M``, ``--always-color`` or ``-C``.
|
||||
* `export MLR_NO_COLOR=true` means Miller won't color even when it normally would.
|
||||
* `export MLR_ALWAYS_COLOR=true` means Miller will color even when it normally would not. For example, you might want to use this when piping `mlr` output to `less -r`.
|
||||
* Command-line flags `--no-color` or `-M`, `--always-color` or `-C`.
|
||||
|
||||
|
||||
* Color choices can be specified by using environment variables or command-line flags, with values 0..255:
|
||||
|
||||
* ``export MLR_KEY_COLOR=208``
|
||||
* ``export MLR_VALUE_COLOR=33``
|
||||
* Likewise for ``MLR_PASS_COLOR``, ``MLR_FAIL_COLOR``, ``MLR_HELP_COLOR``, ``MLR_REPL_PS1_COLOR``, and ``MLR_REPL_PS2_COLOR``.
|
||||
* Command-line flags ``--key-color 208``, ``--value-color 33``, etc., and likewise for ``--pass-color``, ``--fail-color``, ``--repl-ps1-color``, ``--repl-ps2-color``, and ``--help-color``.
|
||||
* `export MLR_KEY_COLOR=208`
|
||||
* `export MLR_VALUE_COLOR=33`
|
||||
* Likewise for `MLR_PASS_COLOR`, `MLR_FAIL_COLOR`, `MLR_HELP_COLOR`, `MLR_REPL_PS1_COLOR`, and `MLR_REPL_PS2_COLOR`.
|
||||
* Command-line flags `--key-color 208`, `--value-color 33`, etc., and likewise for `--pass-color`, `--fail-color`, `--repl-ps1-color`, `--repl-ps2-color`, and `--help-color`.
|
||||
* This is particularly useful if your terminal's background color clashes with current settings.
|
||||
|
||||
If environment-variable settings and command-line flags are both provided, the latter take precedence.
|
||||
|
||||
Please do ``mlr --list-color-codes`` to see the available color codes (like ``170``), and ``mlr --list-color-names`` to see available names (like ``orchid``).
|
||||
|
||||
.. image:: pix/colorization2.png
|
||||
Please do `mlr --list-color-codes` to see the available color codes (like `170`), and `mlr --list-color-names` to see available names (like `orchid`).
|
||||
|
||||

|
||||
|
|
|
|||
|
|
@ -5,14 +5,14 @@
|
|||
|
||||
In a previous version of this page, I compared Miller to some items in the Unix toolkit in terms of run time. But such comparisons are very much not apples-to-apples:
|
||||
|
||||
* Miller's principal strength is that it handles **key-value data in various formats** while the system tools **do not**. So if you time ``mlr sort`` on a CSV file against system ``sort``, it's not relevant to say which is faster by how many percent -- Miller will respect the header line, leaving it in place, while the system sort will move it, sorting it along with all the other header lines. This would be comparing the run times of two programs produce different outputs. Likewise, ``awk`` doesn't respect header lines, although you can code up some CSV-handling using ``if (NR==1) { ... } else { ... }``. And that's just CSV: I don't know any simple way to get ``sort``, ``awk``, etc. to handle DKVP, JSON, etc. -- which is the main reason I wrote Miller.
|
||||
* Miller's principal strength is that it handles **key-value data in various formats** while the system tools **do not**. So if you time `mlr sort` on a CSV file against system `sort`, it's not relevant to say which is faster by how many percent -- Miller will respect the header line, leaving it in place, while the system sort will move it, sorting it along with all the other header lines. This would be comparing the run times of two programs produce different outputs. Likewise, `awk` doesn't respect header lines, although you can code up some CSV-handling using `if (NR==1) { ... } else { ... }`. And that's just CSV: I don't know any simple way to get `sort`, `awk`, etc. to handle DKVP, JSON, etc. -- which is the main reason I wrote Miller.
|
||||
|
||||
* **Implementations differ by platform**: one ``awk`` may be fundamentally faster than another, and ``mawk`` has a very efficient bytecode implementation -- which handles positionally indexed data far faster than Miller does.
|
||||
* **Implementations differ by platform**: one `awk` may be fundamentally faster than another, and `mawk` has a very efficient bytecode implementation -- which handles positionally indexed data far faster than Miller does.
|
||||
|
||||
* The system ``sort`` command will, on some systems, handle too-large-for-RAM datasets by spilling to disk; Miller (as of version 5.2.0, mid-2017) does not. Miller sorts are always stable; GNU supports stable and unstable variants.
|
||||
* The system `sort` command will, on some systems, handle too-large-for-RAM datasets by spilling to disk; Miller (as of version 5.2.0, mid-2017) does not. Miller sorts are always stable; GNU supports stable and unstable variants.
|
||||
|
||||
* Etc.
|
||||
|
||||
## Summary
|
||||
|
||||
Miller can do many kinds of processing on key-value-pair data using elapsed time roughly of the same order of magnitude as items in the Unix toolkit can handle positionally indexed data. Specific results vary widely by platform, implementation details, multi-core use (or not). Lastly, specific special-purpose non-record-aware processing will run far faster in ``grep``, ``sed``, etc.
|
||||
Miller can do many kinds of processing on key-value-pair data using elapsed time roughly of the same order of magnitude as items in the Unix toolkit can handle positionally indexed data. Specific results vary widely by platform, implementation details, multi-core use (or not). Lastly, specific special-purpose non-record-aware processing will run far faster in `grep`, `sed`, etc.
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@
|
|||
|
||||
In a previous version of this page, I compared Miller to some items in the Unix toolkit in terms of run time. But such comparisons are very much not apples-to-apples:
|
||||
|
||||
* Miller's principal strength is that it handles **key-value data in various formats** while the system tools **do not**. So if you time ``mlr sort`` on a CSV file against system ``sort``, it's not relevant to say which is faster by how many percent -- Miller will respect the header line, leaving it in place, while the system sort will move it, sorting it along with all the other header lines. This would be comparing the run times of two programs produce different outputs. Likewise, ``awk`` doesn't respect header lines, although you can code up some CSV-handling using ``if (NR==1) { ... } else { ... }``. And that's just CSV: I don't know any simple way to get ``sort``, ``awk``, etc. to handle DKVP, JSON, etc. -- which is the main reason I wrote Miller.
|
||||
* Miller's principal strength is that it handles **key-value data in various formats** while the system tools **do not**. So if you time `mlr sort` on a CSV file against system `sort`, it's not relevant to say which is faster by how many percent -- Miller will respect the header line, leaving it in place, while the system sort will move it, sorting it along with all the other header lines. This would be comparing the run times of two programs produce different outputs. Likewise, `awk` doesn't respect header lines, although you can code up some CSV-handling using `if (NR==1) { ... } else { ... }`. And that's just CSV: I don't know any simple way to get `sort`, `awk`, etc. to handle DKVP, JSON, etc. -- which is the main reason I wrote Miller.
|
||||
|
||||
* **Implementations differ by platform**: one ``awk`` may be fundamentally faster than another, and ``mawk`` has a very efficient bytecode implementation -- which handles positionally indexed data far faster than Miller does.
|
||||
* **Implementations differ by platform**: one `awk` may be fundamentally faster than another, and `mawk` has a very efficient bytecode implementation -- which handles positionally indexed data far faster than Miller does.
|
||||
|
||||
* The system ``sort`` command will, on some systems, handle too-large-for-RAM datasets by spilling to disk; Miller (as of version 5.2.0, mid-2017) does not. Miller sorts are always stable; GNU supports stable and unstable variants.
|
||||
* The system `sort` command will, on some systems, handle too-large-for-RAM datasets by spilling to disk; Miller (as of version 5.2.0, mid-2017) does not. Miller sorts are always stable; GNU supports stable and unstable variants.
|
||||
|
||||
* Etc.
|
||||
|
||||
## Summary
|
||||
|
||||
Miller can do many kinds of processing on key-value-pair data using elapsed time roughly of the same order of magnitude as items in the Unix toolkit can handle positionally indexed data. Specific results vary widely by platform, implementation details, multi-core use (or not). Lastly, specific special-purpose non-record-aware processing will run far faster in ``grep``, ``sed``, etc.
|
||||
Miller can do many kinds of processing on key-value-pair data using elapsed time roughly of the same order of magnitude as items in the Unix toolkit can handle positionally indexed data. Specific results vary widely by platform, implementation details, multi-core use (or not). Lastly, specific special-purpose non-record-aware processing will run far faster in `grep`, `sed`, etc.
|
||||
|
|
|
|||
|
|
@ -5,10 +5,12 @@ Here are a few things focusing on Miller's DSL as a programming language per se,
|
|||
|
||||
## Sieve of Eratosthenes
|
||||
|
||||
The [Sieve of Eratosthenes](http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes) is a standard introductory programming topic. The idea is to find all primes up to some *N* by making a list of the numbers 1 to *N*, then striking out all multiples of 2 except 2 itself, all multiples of 3 except 3 itself, all multiples of 4 except 4 itself, and so on. Whatever survives that without getting marked is a prime. This is easy enough in Miller. Notice that here all the work is in ``begin`` and ``end`` statements; there is no file input (so we use ``mlr -n`` to keep Miller from waiting for input data).
|
||||
The [Sieve of Eratosthenes](http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes) is a standard introductory programming topic. The idea is to find all primes up to some *N* by making a list of the numbers 1 to *N*, then striking out all multiples of 2 except 2 itself, all multiples of 3 except 3 itself, all multiples of 4 except 4 itself, and so on. Whatever survives that without getting marked is a prime. This is easy enough in Miller. Notice that here all the work is in `begin` and `end` statements; there is no file input (so we use `mlr -n` to keep Miller from waiting for input data).
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat programs/sieve.mlr</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
# ================================================================
|
||||
# Sieve of Eratosthenes: simple example of Miller DSL as programming language.
|
||||
# ================================================================
|
||||
|
|
@ -43,8 +45,10 @@ end {
|
|||
}
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr -n put -f programs/sieve.mlr</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
2
|
||||
3
|
||||
5
|
||||
|
|
@ -78,8 +82,10 @@ The [Mandelbrot set](http://en.wikipedia.org/wiki/Mandelbrot_set) is also easily
|
|||
|
||||
The (approximate) computation of points in the complex plane which are and aren't members is just a few lines of complex arithmetic (see the Wikipedia article); how to render them is another task. Using graphics libraries you can create PNG or JPEG files, but another fun way to do this is by printing various characters to the screen:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat programs/mand.mlr</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
# Mandelbrot set generator: simple example of Miller DSL as programming language.
|
||||
begin {
|
||||
# Set defaults
|
||||
|
|
@ -184,8 +190,10 @@ func get_point_plot(pr, pi, maxits, do_julia, jr, ji) {
|
|||
|
||||
At standard resolution this makes a nice little ASCII plot:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr -n put -f ./programs/mand.mlr</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
|
|
@ -240,7 +248,7 @@ At standard resolution this makes a nice little ASCII plot:
|
|||
|
||||
But using a very small font size (as small as my Mac will let me go), and by choosing the coordinates to zoom in on a particular part of the complex plane, we can get a nice little picture:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
#!/bin/bash
|
||||
# Get the number of rows and columns from the terminal window dimensions
|
||||
iheight=$(stty size | mlr --nidx --fs space cut -f 1)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ Here are a few things focusing on Miller's DSL as a programming language per se,
|
|||
|
||||
## Sieve of Eratosthenes
|
||||
|
||||
The [Sieve of Eratosthenes](http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes) is a standard introductory programming topic. The idea is to find all primes up to some *N* by making a list of the numbers 1 to *N*, then striking out all multiples of 2 except 2 itself, all multiples of 3 except 3 itself, all multiples of 4 except 4 itself, and so on. Whatever survives that without getting marked is a prime. This is easy enough in Miller. Notice that here all the work is in ``begin`` and ``end`` statements; there is no file input (so we use ``mlr -n`` to keep Miller from waiting for input data).
|
||||
The [Sieve of Eratosthenes](http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes) is a standard introductory programming topic. The idea is to find all primes up to some *N* by making a list of the numbers 1 to *N*, then striking out all multiples of 2 except 2 itself, all multiples of 3 except 3 itself, all multiples of 4 except 4 itself, and so on. Whatever survives that without getting marked is a prime. This is easy enough in Miller. Notice that here all the work is in `begin` and `end` statements; there is no file input (so we use `mlr -n` to keep Miller from waiting for input data).
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
cat programs/sieve.mlr
|
||||
|
|
|
|||
|
|
@ -1,18 +1,20 @@
|
|||
<!--- PLEASE DO NOT EDIT DIRECTLY. EDIT THE .md.in FILE PLEASE. --->
|
||||
# Intro to Miller's programming language
|
||||
|
||||
In the [Miller in 10 minutes](10min.md) page we took a tour of some of Miller's most-used verbs including ``cat``, ``head``, ``tail``, ``cut``, and ``sort``. These are analogs of familiar system commands, but empowered by field-name indexing and file-format awareness: the system ``sort`` command only knows about lines and column names like ``1,2,3,4``, while ``mlr sort`` knows about CSV/TSV/JSON/etc records, and field names like ``color,shape,flag,index``.
|
||||
In the [Miller in 10 minutes](10min.md) page we took a tour of some of Miller's most-used verbs including `cat`, `head`, `tail`, `cut`, and `sort`. These are analogs of familiar system commands, but empowered by field-name indexing and file-format awareness: the system `sort` command only knows about lines and column names like `1,2,3,4`, while `mlr sort` knows about CSV/TSV/JSON/etc records, and field names like `color,shape,flag,index`.
|
||||
|
||||
We also caught a glimpse of Miller's ``put`` and ``filter`` verbs. These two are special since they let you express statements using Miller's programming language. It's a *embedded domain-specific language* since it's inside Miller: often referred to simply as the *Miller DSL*.
|
||||
We also caught a glimpse of Miller's `put` and `filter` verbs. These two are special since they let you express statements using Miller's programming language. It's a *embedded domain-specific language* since it's inside Miller: often referred to simply as the *Miller DSL*.
|
||||
|
||||
In the [DSL reference](reference-dsl.md) page we have a complete reference to Miller's programming language. For now, let's take a quick look at key features -- you can use as few or as many features as you like.
|
||||
|
||||
## Records and fields
|
||||
|
||||
Let's keep using the sample [example.csv](./example.csv). When we type
|
||||
Let's keep using the sample [example.csv](./example.csv). For example:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --c2p put '$cost = $quantity * $rate' example.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color shape flag index quantity rate cost
|
||||
yellow triangle true 11 43.6498 9.8870 431.5655726
|
||||
red square true 15 79.2778 0.0130 1.0306114
|
||||
|
|
@ -26,19 +28,21 @@ yellow circle true 87 63.5058 8.3350 529.3208430000001
|
|||
purple square false 91 72.3735 8.2430 596.5747605000001
|
||||
</pre>
|
||||
|
||||
a few things are happening:
|
||||
When we type that, a few things are happening:
|
||||
|
||||
* We refer to fields in the input data using a dollar sign and then the field name, e.g. ``$quantity``. (If a field name has special characters like a dot or slash, just use curly braces: ``${field.name}``.)
|
||||
* The expression ``$cost = $quantity * $rate`` is executed once per record of the data file. Our [example.csv](./example.csv) has 10 records so this expression was executed 10 times, with the field names ``$quantity`` and ``$rate`` bound to the current record's values for those fields.
|
||||
* On the left-hand side we have the new field name ``$cost`` which didn't come from the input data. Assignments to new variables result in a new field being placed after all the other ones. If we'd assigned to an existing field name, it would have been updated in-place.
|
||||
* We refer to fields in the input data using a dollar sign and then the field name, e.g. `$quantity`. (If a field name contains special characters like a dot or slash, just use curly braces: `${field.name}`.)
|
||||
* The expression `$cost = $quantity * $rate` is executed once per record of the data file. Our [example.csv](./example.csv) has 10 records so this expression was executed 10 times, with the field names `$quantity` and `$rate` bound to the current record's values for those fields.
|
||||
* On the left-hand side we have the new field name `$cost` which didn't come from the input data. Assignments to new variables result in a new field being placed after all the other ones. If we'd assigned to an existing field name, it would have been updated in-place.
|
||||
* The entire expression is surrounded by single quotes, to get it past the system shell. Inside those, only double quotes have meaning in Miller's programming language.
|
||||
|
||||
## Multi-line statements, and statements-from-file
|
||||
|
||||
You can use more than one statement, separating them with semicolons, and optionally putting them on lines of their own:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --c2p put '$cost = $quantity * $rate; $index = $index * 100' example.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color shape flag index quantity rate cost
|
||||
yellow triangle true 1100 43.6498 9.8870 431.5655726
|
||||
red square true 1500 79.2778 0.0130 1.0306114
|
||||
|
|
@ -52,11 +56,13 @@ yellow circle true 8700 63.5058 8.3350 529.3208430000001
|
|||
purple square false 9100 72.3735 8.2430 596.5747605000001
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --c2p put '</b>
|
||||
<b> $cost = $quantity * $rate;</b>
|
||||
<b> $index *= 100</b>
|
||||
<b>' example.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color shape flag index quantity rate cost
|
||||
yellow triangle true 1100 43.6498 9.8870 431.5655726
|
||||
red square true 1500 79.2778 0.0130 1.0306114
|
||||
|
|
@ -70,16 +76,20 @@ yellow circle true 8700 63.5058 8.3350 529.3208430000001
|
|||
purple square false 9100 72.3735 8.2430 596.5747605000001
|
||||
</pre>
|
||||
|
||||
One of Miller's key features is the ability to express data-transformation right there at the keyboard, interactively. But if you find yourself using expressions repeatedly, you can put everything between the single quotes into a file and refer to that using ``put -f``:
|
||||
One of Miller's key features is the ability to express data-transformation right there at the keyboard, interactively. But if you find yourself using expressions repeatedly, you can put everything between the single quotes into a file and refer to that using `put -f`:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat dsl-example.mlr</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
$cost = $quantity * $rate;
|
||||
$index *= 100
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --c2p put -f dsl-example.mlr example.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color shape flag index quantity rate cost
|
||||
yellow triangle true 1100 43.6498 9.8870 431.5655726
|
||||
red square true 1500 79.2778 0.0130 1.0306114
|
||||
|
|
@ -93,18 +103,20 @@ yellow circle true 8700 63.5058 8.3350 529.3208430000001
|
|||
purple square false 9100 72.3735 8.2430 596.5747605000001
|
||||
</pre>
|
||||
|
||||
This becomes particularly important on Windows. Quite a bit of effort was put into making Miller on Windows be able to handle the kinds of single-quoted expressions we're showing here, but if you get syntax-error messages on Windows using examples in this documentation, you can put the parts between single quotes into a file and refer to that using ``mlr put -f``.
|
||||
This becomes particularly important on Windows. Quite a bit of effort was put into making Miller on Windows be able to handle the kinds of single-quoted expressions we're showing here, but if you get syntax-error messages on Windows using examples in this documentation, you can put the parts between single quotes into a file and refer to that using `mlr put -f`.
|
||||
|
||||
## Out-of-stream variables, begin, and end
|
||||
|
||||
Above we saw that your expression is executed once per record -- if a file has a million records, your expression will be executed a million times, once for each record. But you can mark statements to only be executed once, either before the record stream begins, or after the record stream is ended. If you know about [AWK](https://en.wikipedia.org/wiki/AWK), you might have noticed that Miller's programming language is loosely inspired by it, including the ``begin`` and ``end`` statements.
|
||||
Above we saw that your expression is executed once per record -- if a file has a million records, your expression will be executed a million times, once for each record. But you can mark statements to only be executed once, either before the record stream begins, or after the record stream is ended. If you know about [AWK](https://en.wikipedia.org/wiki/AWK), you might have noticed that Miller's programming language is loosely inspired by it, including the `begin` and `end` statements.
|
||||
|
||||
Above we also saw that names like ``$quantity`` are bound to each record in turn.
|
||||
Above we also saw that names like `$quantity` are bound to each record in turn.
|
||||
|
||||
To make ``begin`` and ``end`` statements useful, we need somewhere to put things that persist across the duration of the record stream, and a way to emit them. Miller uses **out-of-stream variables** (or **oosvars** for short) whose names start with an ``@`` sigil, and the **emit** keyword to write them into the output record stream:
|
||||
To make `begin` and `end` statements useful, we need somewhere to put things that persist across the duration of the record stream, and a way to emit them. Miller uses **out-of-stream variables** (or **oosvars** for short) whose names start with an `@` sigil, and the **emit** keyword to write them into the output record stream:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --c2p --from example.csv put 'begin { @sum = 0 } @sum += $quantity; end {emit @sum}'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color shape flag index quantity rate
|
||||
yellow triangle true 11 43.6498 9.8870
|
||||
red square true 15 79.2778 0.0130
|
||||
|
|
@ -121,48 +133,56 @@ sum
|
|||
652.7185
|
||||
</pre>
|
||||
|
||||
If you want the end-block output to be the only output, and not include the input data, you can use ``mlr put -q``:
|
||||
If you want the end-block output to be the only output, and not include the input data, you can use `mlr put -q`:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --c2p --from example.csv put -q 'begin { @sum = 0 } @sum += $quantity; end {emit @sum}'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
sum
|
||||
652.7185
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --c2j --from example.csv put -q 'begin { @sum = 0 } @sum += $quantity; end {emit @sum}'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
{
|
||||
"sum": 652.7185
|
||||
}
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --c2j --from example.csv put -q '</b>
|
||||
<b> begin { @count = 0; @sum = 0 }</b>
|
||||
<b> @count += 1;</b>
|
||||
<b> @sum += $quantity;</b>
|
||||
<b> end {emit (@count, @sum)}</b>
|
||||
<b>'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
{
|
||||
"count": 10,
|
||||
"sum": 652.7185
|
||||
}
|
||||
</pre>
|
||||
|
||||
We'll see in the documentation for :ref:`reference-verbs-stats1` that there's a lower-keystroking way to get counts and sums of things -- so, take this sum/count example as an indication of the kinds of things you can do using Miller's programming language.
|
||||
We'll see in the documentation for [stats1](reference-verbs.md#stats1) that there's a lower-keystroking way to get counts and sums of things -- so, take this sum/count example as an indication of the kinds of things you can do using Miller's programming language.
|
||||
|
||||
## Context variables
|
||||
|
||||
Also inspired by [AWK](https://en.wikipedia.org/wiki/AWK), the Miller DSL has the following special **context variables**:
|
||||
|
||||
* ``FILENAME`` -- the filename the current record came from. Especially useful in things like ``mlr ... *.csv``.
|
||||
* ``FILENUM`` -- similarly, but integer 1,2,3,... rather than filenam.e
|
||||
* ``NF`` -- the number of fields in the current record. Note that if you assign ``$newcolumn = some value`` then ``NF`` will increment.
|
||||
* ``NR`` -- starting from 1, counter of how many records processed so far.
|
||||
* ``FNR`` -- similar, but resets to 1 at the start of each file.
|
||||
* `FILENAME` -- the filename the current record came from. Especially useful in things like `mlr ... *.csv`.
|
||||
* `FILENUM` -- similarly, but integer 1,2,3,... rather than filenam.e
|
||||
* `NF` -- the number of fields in the current record. Note that if you assign `$newcolumn = some value` then `NF` will increment.
|
||||
* `NR` -- starting from 1, counter of how many records processed so far.
|
||||
* `FNR` -- similar, but resets to 1 at the start of each file.
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat context-example.mlr</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
$nf = NF;
|
||||
$nr = NR;
|
||||
$fnr = FNR;
|
||||
|
|
@ -171,8 +191,10 @@ $filenum = FILENUM;
|
|||
$newnf = NF;
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --c2p put -f context-example.mlr data/a.csv data/b.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a b c nf nr fnr filename filenum newnf
|
||||
1 2 3 3 1 1 data/a.csv 1 8
|
||||
4 5 6 3 2 2 data/a.csv 1 8
|
||||
|
|
@ -183,8 +205,10 @@ a b c nf nr fnr filename filenum newnf
|
|||
|
||||
You can define your own functions:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat factorial-example.mlr</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
func factorial(n) {
|
||||
if (n <= 1) {
|
||||
return n
|
||||
|
|
@ -194,8 +218,10 @@ func factorial(n) {
|
|||
}
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --c2p --from example.csv put -f factorial-example.mlr -e '$fact = factorial(NR)'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
color shape flag index quantity rate fact
|
||||
yellow triangle true 11 43.6498 9.8870 1
|
||||
red square true 15 79.2778 0.0130 2
|
||||
|
|
@ -209,18 +235,20 @@ yellow circle true 87 63.5058 8.3350 362880
|
|||
purple square false 91 72.3735 8.2430 3628800
|
||||
</pre>
|
||||
|
||||
Note that here we used the ``-f`` flag to ``put`` to load our function
|
||||
definition, and also the ``-e`` flag to add another statement on the command
|
||||
line. (We could have also put ``$fact = factorial(NR)`` inside
|
||||
``factorial-example.mlr`` but that would have made that file less flexible for our
|
||||
Note that here we used the `-f` flag to `put` to load our function
|
||||
definition, and also the `-e` flag to add another statement on the command
|
||||
line. (We could have also put `$fact = factorial(NR)` inside
|
||||
`factorial-example.mlr` but that would have made that file less flexible for our
|
||||
future use.)
|
||||
|
||||
## If-statements, loops, and local variables
|
||||
|
||||
Suppose you want to only compute sums conditionally -- you can use an ``if`` statement:
|
||||
Suppose you want to only compute sums conditionally -- you can use an `if` statement:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat if-example.mlr</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
begin {
|
||||
@count_of_red = 0;
|
||||
@sum_of_red = 0
|
||||
|
|
@ -236,38 +264,46 @@ end {
|
|||
}
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --c2p --from example.csv put -q -f if-example.mlr</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
count_of_red sum_of_red
|
||||
4 247.84139999999996
|
||||
</pre>
|
||||
|
||||
Miller's else-if is spelled ``elif``.
|
||||
Miller's else-if is spelled `elif`.
|
||||
|
||||
As we'll see more of in section (TODO:linkify), Miller has a few kinds of
|
||||
for-loops. In addition to the usual 3-part ``for (i = 0; i < 10; i += 1)`` kind
|
||||
for-loops. In addition to the usual 3-part `for (i = 0; i < 10; i += 1)` kind
|
||||
that many programming languages have, Miller also lets you loop over arrays and
|
||||
hashmaps. We haven't encountered arrays and hashmaps yet in this introduction,
|
||||
but for now it suffices to know that ``$*`` is a special variable holding the
|
||||
but for now it suffices to know that `$*` is a special variable holding the
|
||||
current record as a hashmap:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat for-example.mlr</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
for (k, v in $*) {
|
||||
print "KEY IS ". k . " VALUE IS ". v;
|
||||
}
|
||||
print
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --csv cat data/a.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a,b,c
|
||||
1,2,3
|
||||
4,5,6
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --csv --from data/a.csv put -qf for-example.mlr</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
KEY IS a VALUE IS 1
|
||||
KEY IS b VALUE IS 2
|
||||
KEY IS c VALUE IS 3
|
||||
|
|
@ -277,12 +313,12 @@ KEY IS b VALUE IS 5
|
|||
KEY IS c VALUE IS 6
|
||||
</pre>
|
||||
|
||||
Here we used the local variables ``k`` and ``v``. Now we've seen four kinds of variables:
|
||||
Here we used the local variables `k` and `v`. Now we've seen four kinds of variables:
|
||||
|
||||
* Record fields like ``$shape``
|
||||
* Out-of-stream variables like ``@sum``
|
||||
* Local variables like ``k``
|
||||
* Built-in context variables like ``NF`` and ``NR``
|
||||
* Record fields like `$shape`
|
||||
* Out-of-stream variables like `@sum`
|
||||
* Local variables like `k`
|
||||
* Built-in context variables like `NF` and `NR`
|
||||
|
||||
If you're curious about scope and extent of local variables, you can read more in (TODO:linkify) the section on variables.
|
||||
|
||||
|
|
@ -291,8 +327,8 @@ If you're curious about scope and extent of local variables, you can read more i
|
|||
Numbers in Miller's programming language are intended to operate with the principle of least surprise:
|
||||
|
||||
* Internally, numbers are either 64-bit signed integers or double-precision floating-point.
|
||||
* Sums, differences, and products of integers are also integers (so ``2*3=6`` not ``6.0``) -- unless the result of the operation would overflow a 64-bit signed integer in which case the result is automatically converted to float. (If you ever want integer-to-integer arithmetic, use ``x .+ y``, ``x .* y``, etc.)
|
||||
* Quotients of integers are integers if the division is exact, else floating-point: so ``6/2=3`` but ``7/2=3.5``.
|
||||
* Sums, differences, and products of integers are also integers (so `2*3=6` not `6.0`) -- unless the result of the operation would overflow a 64-bit signed integer in which case the result is automatically converted to float. (If you ever want integer-to-integer arithmetic, use `x .+ y`, `x .* y`, etc.)
|
||||
* Quotients of integers are integers if the division is exact, else floating-point: so `6/2=3` but `7/2=3.5`.
|
||||
|
||||
You can read more about this at (TODO:linkify).
|
||||
|
||||
|
|
@ -300,13 +336,15 @@ You can read more about this at (TODO:linkify).
|
|||
|
||||
In addition to types including string, number (int/float), arrays, and hashmaps, Miller varibles can also be **absent**. This is when a variable never had a value assigned to it. Miller's treatment of absent data is intended to make it easy for you to handle non-heterogeneous data. We'll see more in section (TODO:linkify) but the basic idea is:
|
||||
|
||||
* Adding a number to absent gives the number back. This means you don't have to put ``@sum = 0`` in your ``begin`` blocks.
|
||||
* Adding a number to absent gives the number back. This means you don't have to put `@sum = 0` in your `begin` blocks.
|
||||
* Any variable which has the absent value is not assigned. This means you don't have to check presence of things from one record to the next.
|
||||
|
||||
For example, you can sum up all the ``$a`` values across records without having to check whether they're present or not:
|
||||
For example, you can sum up all the `$a` values across records without having to check whether they're present or not:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --json cat absent-example.json</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
{
|
||||
"a": 1,
|
||||
"b": 2
|
||||
|
|
@ -320,8 +358,10 @@ For example, you can sum up all the ``$a`` values across records without having
|
|||
}
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --json put '@sum_of_a += $a; end {emit @sum_of_a}' absent-example.json</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
{
|
||||
"a": 1,
|
||||
"b": 2
|
||||
|
|
|
|||
|
|
@ -1,24 +1,24 @@
|
|||
# Intro to Miller's programming language
|
||||
|
||||
In the [Miller in 10 minutes](10min.md) page we took a tour of some of Miller's most-used verbs including ``cat``, ``head``, ``tail``, ``cut``, and ``sort``. These are analogs of familiar system commands, but empowered by field-name indexing and file-format awareness: the system ``sort`` command only knows about lines and column names like ``1,2,3,4``, while ``mlr sort`` knows about CSV/TSV/JSON/etc records, and field names like ``color,shape,flag,index``.
|
||||
In the [Miller in 10 minutes](10min.md) page we took a tour of some of Miller's most-used verbs including `cat`, `head`, `tail`, `cut`, and `sort`. These are analogs of familiar system commands, but empowered by field-name indexing and file-format awareness: the system `sort` command only knows about lines and column names like `1,2,3,4`, while `mlr sort` knows about CSV/TSV/JSON/etc records, and field names like `color,shape,flag,index`.
|
||||
|
||||
We also caught a glimpse of Miller's ``put`` and ``filter`` verbs. These two are special since they let you express statements using Miller's programming language. It's a *embedded domain-specific language* since it's inside Miller: often referred to simply as the *Miller DSL*.
|
||||
We also caught a glimpse of Miller's `put` and `filter` verbs. These two are special since they let you express statements using Miller's programming language. It's a *embedded domain-specific language* since it's inside Miller: often referred to simply as the *Miller DSL*.
|
||||
|
||||
In the [DSL reference](reference-dsl.md) page we have a complete reference to Miller's programming language. For now, let's take a quick look at key features -- you can use as few or as many features as you like.
|
||||
|
||||
## Records and fields
|
||||
|
||||
Let's keep using the sample [example.csv](./example.csv). When we type
|
||||
Let's keep using the sample [example.csv](./example.csv). For example:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --c2p put '$cost = $quantity * $rate' example.csv
|
||||
GENMD_EOF
|
||||
|
||||
a few things are happening:
|
||||
When we type that, a few things are happening:
|
||||
|
||||
* We refer to fields in the input data using a dollar sign and then the field name, e.g. ``$quantity``. (If a field name has special characters like a dot or slash, just use curly braces: ``${field.name}``.)
|
||||
* The expression ``$cost = $quantity * $rate`` is executed once per record of the data file. Our [example.csv](./example.csv) has 10 records so this expression was executed 10 times, with the field names ``$quantity`` and ``$rate`` bound to the current record's values for those fields.
|
||||
* On the left-hand side we have the new field name ``$cost`` which didn't come from the input data. Assignments to new variables result in a new field being placed after all the other ones. If we'd assigned to an existing field name, it would have been updated in-place.
|
||||
* We refer to fields in the input data using a dollar sign and then the field name, e.g. `$quantity`. (If a field name contains special characters like a dot or slash, just use curly braces: `${field.name}`.)
|
||||
* The expression `$cost = $quantity * $rate` is executed once per record of the data file. Our [example.csv](./example.csv) has 10 records so this expression was executed 10 times, with the field names `$quantity` and `$rate` bound to the current record's values for those fields.
|
||||
* On the left-hand side we have the new field name `$cost` which didn't come from the input data. Assignments to new variables result in a new field being placed after all the other ones. If we'd assigned to an existing field name, it would have been updated in-place.
|
||||
* The entire expression is surrounded by single quotes, to get it past the system shell. Inside those, only double quotes have meaning in Miller's programming language.
|
||||
|
||||
## Multi-line statements, and statements-from-file
|
||||
|
|
@ -31,7 +31,7 @@ GENMD_EOF
|
|||
|
||||
GENMD_INCLUDE_AND_RUN_ESCAPED(dsl-example-multiline.sh)
|
||||
|
||||
One of Miller's key features is the ability to express data-transformation right there at the keyboard, interactively. But if you find yourself using expressions repeatedly, you can put everything between the single quotes into a file and refer to that using ``put -f``:
|
||||
One of Miller's key features is the ability to express data-transformation right there at the keyboard, interactively. But if you find yourself using expressions repeatedly, you can put everything between the single quotes into a file and refer to that using `put -f`:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
cat dsl-example.mlr
|
||||
|
|
@ -41,21 +41,21 @@ GENMD_RUN_COMMAND
|
|||
mlr --c2p put -f dsl-example.mlr example.csv
|
||||
GENMD_EOF
|
||||
|
||||
This becomes particularly important on Windows. Quite a bit of effort was put into making Miller on Windows be able to handle the kinds of single-quoted expressions we're showing here, but if you get syntax-error messages on Windows using examples in this documentation, you can put the parts between single quotes into a file and refer to that using ``mlr put -f``.
|
||||
This becomes particularly important on Windows. Quite a bit of effort was put into making Miller on Windows be able to handle the kinds of single-quoted expressions we're showing here, but if you get syntax-error messages on Windows using examples in this documentation, you can put the parts between single quotes into a file and refer to that using `mlr put -f`.
|
||||
|
||||
## Out-of-stream variables, begin, and end
|
||||
|
||||
Above we saw that your expression is executed once per record -- if a file has a million records, your expression will be executed a million times, once for each record. But you can mark statements to only be executed once, either before the record stream begins, or after the record stream is ended. If you know about [AWK](https://en.wikipedia.org/wiki/AWK), you might have noticed that Miller's programming language is loosely inspired by it, including the ``begin`` and ``end`` statements.
|
||||
Above we saw that your expression is executed once per record -- if a file has a million records, your expression will be executed a million times, once for each record. But you can mark statements to only be executed once, either before the record stream begins, or after the record stream is ended. If you know about [AWK](https://en.wikipedia.org/wiki/AWK), you might have noticed that Miller's programming language is loosely inspired by it, including the `begin` and `end` statements.
|
||||
|
||||
Above we also saw that names like ``$quantity`` are bound to each record in turn.
|
||||
Above we also saw that names like `$quantity` are bound to each record in turn.
|
||||
|
||||
To make ``begin`` and ``end`` statements useful, we need somewhere to put things that persist across the duration of the record stream, and a way to emit them. Miller uses **out-of-stream variables** (or **oosvars** for short) whose names start with an ``@`` sigil, and the **emit** keyword to write them into the output record stream:
|
||||
To make `begin` and `end` statements useful, we need somewhere to put things that persist across the duration of the record stream, and a way to emit them. Miller uses **out-of-stream variables** (or **oosvars** for short) whose names start with an `@` sigil, and the **emit** keyword to write them into the output record stream:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --c2p --from example.csv put 'begin { @sum = 0 } @sum += $quantity; end {emit @sum}'
|
||||
GENMD_EOF
|
||||
|
||||
If you want the end-block output to be the only output, and not include the input data, you can use ``mlr put -q``:
|
||||
If you want the end-block output to be the only output, and not include the input data, you can use `mlr put -q`:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --c2p --from example.csv put -q 'begin { @sum = 0 } @sum += $quantity; end {emit @sum}'
|
||||
|
|
@ -74,17 +74,17 @@ mlr --c2j --from example.csv put -q '
|
|||
'
|
||||
GENMD_EOF
|
||||
|
||||
We'll see in the documentation for :ref:`reference-verbs-stats1` that there's a lower-keystroking way to get counts and sums of things -- so, take this sum/count example as an indication of the kinds of things you can do using Miller's programming language.
|
||||
We'll see in the documentation for [stats1](reference-verbs.md#stats1) that there's a lower-keystroking way to get counts and sums of things -- so, take this sum/count example as an indication of the kinds of things you can do using Miller's programming language.
|
||||
|
||||
## Context variables
|
||||
|
||||
Also inspired by [AWK](https://en.wikipedia.org/wiki/AWK), the Miller DSL has the following special **context variables**:
|
||||
|
||||
* ``FILENAME`` -- the filename the current record came from. Especially useful in things like ``mlr ... *.csv``.
|
||||
* ``FILENUM`` -- similarly, but integer 1,2,3,... rather than filenam.e
|
||||
* ``NF`` -- the number of fields in the current record. Note that if you assign ``$newcolumn = some value`` then ``NF`` will increment.
|
||||
* ``NR`` -- starting from 1, counter of how many records processed so far.
|
||||
* ``FNR`` -- similar, but resets to 1 at the start of each file.
|
||||
* `FILENAME` -- the filename the current record came from. Especially useful in things like `mlr ... *.csv`.
|
||||
* `FILENUM` -- similarly, but integer 1,2,3,... rather than filenam.e
|
||||
* `NF` -- the number of fields in the current record. Note that if you assign `$newcolumn = some value` then `NF` will increment.
|
||||
* `NR` -- starting from 1, counter of how many records processed so far.
|
||||
* `FNR` -- similar, but resets to 1 at the start of each file.
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
cat context-example.mlr
|
||||
|
|
@ -106,15 +106,15 @@ GENMD_RUN_COMMAND
|
|||
mlr --c2p --from example.csv put -f factorial-example.mlr -e '$fact = factorial(NR)'
|
||||
GENMD_EOF
|
||||
|
||||
Note that here we used the ``-f`` flag to ``put`` to load our function
|
||||
definition, and also the ``-e`` flag to add another statement on the command
|
||||
line. (We could have also put ``$fact = factorial(NR)`` inside
|
||||
``factorial-example.mlr`` but that would have made that file less flexible for our
|
||||
Note that here we used the `-f` flag to `put` to load our function
|
||||
definition, and also the `-e` flag to add another statement on the command
|
||||
line. (We could have also put `$fact = factorial(NR)` inside
|
||||
`factorial-example.mlr` but that would have made that file less flexible for our
|
||||
future use.)
|
||||
|
||||
## If-statements, loops, and local variables
|
||||
|
||||
Suppose you want to only compute sums conditionally -- you can use an ``if`` statement:
|
||||
Suppose you want to only compute sums conditionally -- you can use an `if` statement:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
cat if-example.mlr
|
||||
|
|
@ -124,13 +124,13 @@ GENMD_RUN_COMMAND
|
|||
mlr --c2p --from example.csv put -q -f if-example.mlr
|
||||
GENMD_EOF
|
||||
|
||||
Miller's else-if is spelled ``elif``.
|
||||
Miller's else-if is spelled `elif`.
|
||||
|
||||
As we'll see more of in section (TODO:linkify), Miller has a few kinds of
|
||||
for-loops. In addition to the usual 3-part ``for (i = 0; i < 10; i += 1)`` kind
|
||||
for-loops. In addition to the usual 3-part `for (i = 0; i < 10; i += 1)` kind
|
||||
that many programming languages have, Miller also lets you loop over arrays and
|
||||
hashmaps. We haven't encountered arrays and hashmaps yet in this introduction,
|
||||
but for now it suffices to know that ``$*`` is a special variable holding the
|
||||
but for now it suffices to know that `$*` is a special variable holding the
|
||||
current record as a hashmap:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
|
|
@ -145,12 +145,12 @@ GENMD_RUN_COMMAND
|
|||
mlr --csv --from data/a.csv put -qf for-example.mlr
|
||||
GENMD_EOF
|
||||
|
||||
Here we used the local variables ``k`` and ``v``. Now we've seen four kinds of variables:
|
||||
Here we used the local variables `k` and `v`. Now we've seen four kinds of variables:
|
||||
|
||||
* Record fields like ``$shape``
|
||||
* Out-of-stream variables like ``@sum``
|
||||
* Local variables like ``k``
|
||||
* Built-in context variables like ``NF`` and ``NR``
|
||||
* Record fields like `$shape`
|
||||
* Out-of-stream variables like `@sum`
|
||||
* Local variables like `k`
|
||||
* Built-in context variables like `NF` and `NR`
|
||||
|
||||
If you're curious about scope and extent of local variables, you can read more in (TODO:linkify) the section on variables.
|
||||
|
||||
|
|
@ -159,8 +159,8 @@ If you're curious about scope and extent of local variables, you can read more i
|
|||
Numbers in Miller's programming language are intended to operate with the principle of least surprise:
|
||||
|
||||
* Internally, numbers are either 64-bit signed integers or double-precision floating-point.
|
||||
* Sums, differences, and products of integers are also integers (so ``2*3=6`` not ``6.0``) -- unless the result of the operation would overflow a 64-bit signed integer in which case the result is automatically converted to float. (If you ever want integer-to-integer arithmetic, use ``x .+ y``, ``x .* y``, etc.)
|
||||
* Quotients of integers are integers if the division is exact, else floating-point: so ``6/2=3`` but ``7/2=3.5``.
|
||||
* Sums, differences, and products of integers are also integers (so `2*3=6` not `6.0`) -- unless the result of the operation would overflow a 64-bit signed integer in which case the result is automatically converted to float. (If you ever want integer-to-integer arithmetic, use `x .+ y`, `x .* y`, etc.)
|
||||
* Quotients of integers are integers if the division is exact, else floating-point: so `6/2=3` but `7/2=3.5`.
|
||||
|
||||
You can read more about this at (TODO:linkify).
|
||||
|
||||
|
|
@ -168,10 +168,10 @@ You can read more about this at (TODO:linkify).
|
|||
|
||||
In addition to types including string, number (int/float), arrays, and hashmaps, Miller varibles can also be **absent**. This is when a variable never had a value assigned to it. Miller's treatment of absent data is intended to make it easy for you to handle non-heterogeneous data. We'll see more in section (TODO:linkify) but the basic idea is:
|
||||
|
||||
* Adding a number to absent gives the number back. This means you don't have to put ``@sum = 0`` in your ``begin`` blocks.
|
||||
* Adding a number to absent gives the number back. This means you don't have to put `@sum = 0` in your `begin` blocks.
|
||||
* Any variable which has the absent value is not assigned. This means you don't have to check presence of things from one record to the next.
|
||||
|
||||
For example, you can sum up all the ``$a`` values across records without having to check whether they're present or not:
|
||||
For example, you can sum up all the `$a` values across records without having to check whether they're present or not:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --json cat absent-example.json
|
||||
|
|
|
|||
|
|
@ -5,8 +5,10 @@
|
|||
|
||||
Here we can chain together a few simple building blocks:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat expo-sample.sh</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
# Generate 100,000 pairs of independent and identically distributed
|
||||
# exponentially distributed random variables with the same rate parameter
|
||||
# (namely, 2.5). Then compute histograms of one of them, along with
|
||||
|
|
@ -40,14 +42,16 @@ Namely:
|
|||
* Set the Miller random-number seed so this webdoc looks the same every time I regenerate it.
|
||||
* Use pretty-printed tabular output.
|
||||
* Use pretty-printed tabular output.
|
||||
* Use ``seqgen`` to produce 100,000 records ``i=0``, ``i=1``, etc.
|
||||
* Send those to a ``put`` step which defines an inverse-transform-sampling function and calls it twice, then computes the sum and product of samples.
|
||||
* Use `seqgen` to produce 100,000 records `i=0`, `i=1`, etc.
|
||||
* Send those to a `put` step which defines an inverse-transform-sampling function and calls it twice, then computes the sum and product of samples.
|
||||
* Send those to a histogram, and from there to a bar-plotter. This is just for visualization; you could just as well output CSV and send that off to your own plotting tool, etc.
|
||||
|
||||
The output is as follows:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>sh expo-sample.sh</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
bin_lo bin_hi u_count s_count
|
||||
0 0.04 [64]*******************#[9554] [326]#...................[3703]
|
||||
0.04 0.08 [64]*****************...[9554] [326]*****...............[3703]
|
||||
|
|
@ -105,8 +109,10 @@ bin_lo bin_hi u_count s_count
|
|||
|
||||
Given this [word list](./data/english-words.txt), first take a look to see what the first few lines look like:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>head data/english-words.txt</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a
|
||||
aa
|
||||
aal
|
||||
|
|
@ -121,8 +127,10 @@ abaca
|
|||
|
||||
Then the following will randomly sample ten words with four to eight characters in them:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from data/english-words.txt --nidx filter -S 'n=strlen($1);4<=n&&n<=8' then sample -k 10</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
thionine
|
||||
birchman
|
||||
mildewy
|
||||
|
|
@ -141,8 +149,10 @@ These are simple *n*-grams as [described here](http://johnkerl.org/randspell/ran
|
|||
|
||||
The idea is that words from the input file are consumed, then taken apart and pasted back together in ways which imitate the letter-to-letter transitions found in the word list -- giving us automatically generated words in the same vein as *bromance* and *spork*:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --nidx --from ./ngrams/gsl-2000.txt put -q -f ./ngrams/ngfuncs.mlr -f ./ngrams/ng5.mlr</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
beard
|
||||
plastinguish
|
||||
politicially
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ Namely:
|
|||
* Set the Miller random-number seed so this webdoc looks the same every time I regenerate it.
|
||||
* Use pretty-printed tabular output.
|
||||
* Use pretty-printed tabular output.
|
||||
* Use ``seqgen`` to produce 100,000 records ``i=0``, ``i=1``, etc.
|
||||
* Send those to a ``put`` step which defines an inverse-transform-sampling function and calls it twice, then computes the sum and product of samples.
|
||||
* Use `seqgen` to produce 100,000 records `i=0`, `i=1`, etc.
|
||||
* Send those to a `put` step which defines an inverse-transform-sampling function and calls it twice, then computes the sum and product of samples.
|
||||
* Send those to a histogram, and from there to a bar-plotter. This is just for visualization; you could just as well output CSV and send that off to your own plotting tool, etc.
|
||||
|
||||
The output is as follows:
|
||||
|
|
|
|||
|
|
@ -11,8 +11,10 @@ But heterogeneous data abound (today's no-SQL databases for example). Miller han
|
|||
|
||||
Miller simply prints a newline and a new header when there is a schema change. When there is no schema change, you get CSV per se as a special case. Likewise, Miller reads heterogeneous CSV or pretty-print input the same way. The difference between CSV and CSV-lite is that the former is RFC4180-compliant, while the latter readily handles heterogeneous data (which is non-compliant). For example:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/het.dkvp</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
resource=/path/to/file,loadsec=0.45,ok=true
|
||||
record_count=100,resource=/path/to/file
|
||||
resource=/path/to/second/file,loadsec=0.32,ok=true
|
||||
|
|
@ -20,8 +22,10 @@ record_count=150,resource=/path/to/second/file
|
|||
resource=/some/other/path,loadsec=0.97,ok=false
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --ocsvlite cat data/het.dkvp</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
resource,loadsec,ok
|
||||
/path/to/file,0.45,true
|
||||
|
||||
|
|
@ -38,8 +42,10 @@ resource,loadsec,ok
|
|||
/some/other/path,0.97,false
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --opprint cat data/het.dkvp</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
resource loadsec ok
|
||||
/path/to/file 0.45 true
|
||||
|
||||
|
|
@ -56,18 +62,22 @@ resource loadsec ok
|
|||
/some/other/path 0.97 false
|
||||
</pre>
|
||||
|
||||
Miller handles explicit header changes as just shown. If your CSV input contains ragged data -- if there are implicit header changes -- you can use ``--allow-ragged-csv-input`` (or keystroke-saver ``--ragged``). For too-short data lines, values are filled with empty string; for too-long data lines, missing field names are replaced with positional indices (counting up from 1, not 0), as follows:
|
||||
Miller handles explicit header changes as just shown. If your CSV input contains ragged data -- if there are implicit header changes -- you can use `--allow-ragged-csv-input` (or keystroke-saver `--ragged`). For too-short data lines, values are filled with empty string; for too-long data lines, missing field names are replaced with positional indices (counting up from 1, not 0), as follows:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/ragged.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a,b,c
|
||||
1,2,3
|
||||
4,5
|
||||
6,7,8,9
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --icsv --oxtab --allow-ragged-csv-input cat data/ragged.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a 1
|
||||
b 2
|
||||
c 3
|
||||
|
|
@ -82,10 +92,12 @@ c 8
|
|||
4 9
|
||||
</pre>
|
||||
|
||||
You may also find Miller's ``group-like`` feature handy (see also [Verbs Reference](reference-verbs.md)):
|
||||
You may also find Miller's `group-like` feature handy (see also [Verbs Reference](reference-verbs.md)):
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --ocsvlite group-like data/het.dkvp</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
resource,loadsec,ok
|
||||
/path/to/file,0.45,true
|
||||
/path/to/second/file,0.32,true
|
||||
|
|
@ -96,8 +108,10 @@ record_count,resource
|
|||
150,/path/to/second/file
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --opprint group-like data/het.dkvp</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
resource loadsec ok
|
||||
/path/to/file 0.45 true
|
||||
/path/to/second/file 0.32 true
|
||||
|
|
@ -112,8 +126,10 @@ record_count resource
|
|||
|
||||
For these formats, record-heterogeneity comes naturally:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/het.dkvp</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
resource=/path/to/file,loadsec=0.45,ok=true
|
||||
record_count=100,resource=/path/to/file
|
||||
resource=/path/to/second/file,loadsec=0.32,ok=true
|
||||
|
|
@ -121,8 +137,10 @@ record_count=150,resource=/path/to/second/file
|
|||
resource=/some/other/path,loadsec=0.97,ok=false
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --onidx --ofs ' ' cat data/het.dkvp</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
/path/to/file 0.45 true
|
||||
100 /path/to/file
|
||||
/path/to/second/file 0.32 true
|
||||
|
|
@ -130,8 +148,10 @@ resource=/some/other/path,loadsec=0.97,ok=false
|
|||
/some/other/path 0.97 false
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --oxtab cat data/het.dkvp</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
resource /path/to/file
|
||||
loadsec 0.45
|
||||
ok true
|
||||
|
|
@ -151,8 +171,10 @@ loadsec 0.97
|
|||
ok false
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --oxtab group-like data/het.dkvp</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
resource /path/to/file
|
||||
loadsec 0.45
|
||||
ok true
|
||||
|
|
@ -174,10 +196,12 @@ resource /path/to/second/file
|
|||
|
||||
## For processing
|
||||
|
||||
Miller operates on specified fields and takes the rest along: for example, if you are sorting on the ``count`` field then all records in the input stream must have a ``count`` field but the other fields can vary, and moreover the sorted-on field name(s) don't need to be in the same position on each line:
|
||||
Miller operates on specified fields and takes the rest along: for example, if you are sorting on the `count` field then all records in the input stream must have a `count` field but the other fields can vary, and moreover the sorted-on field name(s) don't need to be in the same position on each line:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/sort-het.dkvp</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
count=500,color=green
|
||||
count=600
|
||||
status=ok,count=250,hours=0.22
|
||||
|
|
@ -187,8 +211,10 @@ count=100,color=green
|
|||
count=450
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr sort -n count data/sort-het.dkvp</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
count=100,color=green
|
||||
status=ok,count=200,hours=3.4
|
||||
status=ok,count=250,hours=0.22
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ GENMD_RUN_COMMAND
|
|||
mlr --opprint cat data/het.dkvp
|
||||
GENMD_EOF
|
||||
|
||||
Miller handles explicit header changes as just shown. If your CSV input contains ragged data -- if there are implicit header changes -- you can use ``--allow-ragged-csv-input`` (or keystroke-saver ``--ragged``). For too-short data lines, values are filled with empty string; for too-long data lines, missing field names are replaced with positional indices (counting up from 1, not 0), as follows:
|
||||
Miller handles explicit header changes as just shown. If your CSV input contains ragged data -- if there are implicit header changes -- you can use `--allow-ragged-csv-input` (or keystroke-saver `--ragged`). For too-short data lines, values are filled with empty string; for too-long data lines, missing field names are replaced with positional indices (counting up from 1, not 0), as follows:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
cat data/ragged.csv
|
||||
|
|
@ -32,7 +32,7 @@ GENMD_RUN_COMMAND
|
|||
mlr --icsv --oxtab --allow-ragged-csv-input cat data/ragged.csv
|
||||
GENMD_EOF
|
||||
|
||||
You may also find Miller's ``group-like`` feature handy (see also [Verbs Reference](reference-verbs.md)):
|
||||
You may also find Miller's `group-like` feature handy (see also [Verbs Reference](reference-verbs.md)):
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --ocsvlite group-like data/het.dkvp
|
||||
|
|
@ -64,7 +64,7 @@ GENMD_EOF
|
|||
|
||||
## For processing
|
||||
|
||||
Miller operates on specified fields and takes the rest along: for example, if you are sorting on the ``count`` field then all records in the input stream must have a ``count`` field but the other fields can vary, and moreover the sorted-on field name(s) don't need to be in the same position on each line:
|
||||
Miller operates on specified fields and takes the rest along: for example, if you are sorting on the `count` field then all records in the input stream must have a `count` field but the other fields can vary, and moreover the sorted-on field name(s) don't need to be in the same position on each line:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
cat data/sort-het.dkvp
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@
|
|||
|
||||
TODO
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --json cat data/array-example.json</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
{
|
||||
"key": "ax04",
|
||||
"samples": [45, 67, 22]
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -6,7 +6,7 @@ GENMD_RUN_CONTENT_GENERATOR(mk-func-table.rb)
|
|||
|
||||
## List of functions
|
||||
|
||||
Each function takes a specific number of arguments, as shown below, except for functions marked as variadic such as ``min`` and ``max``. (The latter compute min and max of any number of numerical arguments.) There is no notion of optional or default-on-absent arguments. All argument-passing is positional rather than by name; arguments are passed by value, not by reference.
|
||||
Each function takes a specific number of arguments, as shown below, except for functions marked as variadic such as `min` and `max`. (The latter compute min and max of any number of numerical arguments.) There is no notion of optional or default-on-absent arguments. All argument-passing is positional rather than by name; arguments are passed by value, not by reference.
|
||||
|
||||
You can get a list of all functions using **mlr -f**, with details using **mlr -F**.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
<!--- PLEASE DO NOT EDIT DIRECTLY. EDIT THE .md.in FILE PLEASE. --->
|
||||
# DSL reference: a note on the complexity of Miller's expression language
|
||||
|
||||
One of Miller's strengths is its brevity: it's much quicker -- and less error-prone -- to type ``mlr stats1 -a sum -f x,y -g a,b`` than having to track summation variables as in ``awk``, or using Miller's out-of-stream variables. And the more language features Miller's put-DSL has (for-loops, if-statements, nested control structures, user-defined functions, etc.) then the *less* powerful it begins to seem: because of the other programming-language features it *doesn't* have (classes, exceptions, and so on).
|
||||
One of Miller's strengths is its brevity: it's much quicker -- and less error-prone -- to type `mlr stats1 -a sum -f x,y -g a,b` than having to track summation variables as in `awk`, or using Miller's out-of-stream variables. And the more language features Miller's put-DSL has (for-loops, if-statements, nested control structures, user-defined functions, etc.) then the *less* powerful it begins to seem: because of the other programming-language features it *doesn't* have (classes, exceptions, and so on).
|
||||
|
||||
When I was originally prototyping Miller in 2015, the decision I had was whether to hand-code in a low-level language like C or Rust or Go, with my own hand-rolled DSL, or whether to use a higher-level language (like Python or Lua or Nim) and let the ``put`` statements be handled by the implementation language's own ``eval``: the implementation language would take the place of a DSL. Multiple performance experiments showed me I could get better throughput using the former, by a wide margin. So Miller is Go under the hood with a hand-rolled DSL.
|
||||
When I was originally prototyping Miller in 2015, the decision I had was whether to hand-code in a low-level language like C or Rust or Go, with my own hand-rolled DSL, or whether to use a higher-level language (like Python or Lua or Nim) and let the `put` statements be handled by the implementation language's own `eval`: the implementation language would take the place of a DSL. Multiple performance experiments showed me I could get better throughput using the former, by a wide margin. So Miller is Go under the hood with a hand-rolled DSL.
|
||||
|
||||
I do want to keep focusing on what Miller is good at -- concise notation, low latency, and high throughput -- and not add too much in terms of high-level-language features to the DSL. That said, some sort of customizability is a basic thing to want. As of 4.1.0 we have recursive for/while/if structures on about the same complexity level as ``awk``; as of 5.0.0 we have user-defined functions and map-valued variables, again on about the same complexity level as ``awk`` along with optional type-declaration syntax; as of Miller 6 we have full support for arrays. While I'm excited by these powerful language features, I hope to keep new features focused on Miller's sweet spot which is speed plus simplicity.
|
||||
I do want to keep focusing on what Miller is good at -- concise notation, low latency, and high throughput -- and not add too much in terms of high-level-language features to the DSL. That said, some sort of customizability is a basic thing to want. As of 4.1.0 we have recursive for/while/if structures on about the same complexity level as `awk`; as of 5.0.0 we have user-defined functions and map-valued variables, again on about the same complexity level as `awk` along with optional type-declaration syntax; as of Miller 6 we have full support for arrays. While I'm excited by these powerful language features, I hope to keep new features focused on Miller's sweet spot which is speed plus simplicity.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# DSL reference: a note on the complexity of Miller's expression language
|
||||
|
||||
One of Miller's strengths is its brevity: it's much quicker -- and less error-prone -- to type ``mlr stats1 -a sum -f x,y -g a,b`` than having to track summation variables as in ``awk``, or using Miller's out-of-stream variables. And the more language features Miller's put-DSL has (for-loops, if-statements, nested control structures, user-defined functions, etc.) then the *less* powerful it begins to seem: because of the other programming-language features it *doesn't* have (classes, exceptions, and so on).
|
||||
One of Miller's strengths is its brevity: it's much quicker -- and less error-prone -- to type `mlr stats1 -a sum -f x,y -g a,b` than having to track summation variables as in `awk`, or using Miller's out-of-stream variables. And the more language features Miller's put-DSL has (for-loops, if-statements, nested control structures, user-defined functions, etc.) then the *less* powerful it begins to seem: because of the other programming-language features it *doesn't* have (classes, exceptions, and so on).
|
||||
|
||||
When I was originally prototyping Miller in 2015, the decision I had was whether to hand-code in a low-level language like C or Rust or Go, with my own hand-rolled DSL, or whether to use a higher-level language (like Python or Lua or Nim) and let the ``put`` statements be handled by the implementation language's own ``eval``: the implementation language would take the place of a DSL. Multiple performance experiments showed me I could get better throughput using the former, by a wide margin. So Miller is Go under the hood with a hand-rolled DSL.
|
||||
When I was originally prototyping Miller in 2015, the decision I had was whether to hand-code in a low-level language like C or Rust or Go, with my own hand-rolled DSL, or whether to use a higher-level language (like Python or Lua or Nim) and let the `put` statements be handled by the implementation language's own `eval`: the implementation language would take the place of a DSL. Multiple performance experiments showed me I could get better throughput using the former, by a wide margin. So Miller is Go under the hood with a hand-rolled DSL.
|
||||
|
||||
I do want to keep focusing on what Miller is good at -- concise notation, low latency, and high throughput -- and not add too much in terms of high-level-language features to the DSL. That said, some sort of customizability is a basic thing to want. As of 4.1.0 we have recursive for/while/if structures on about the same complexity level as ``awk``; as of 5.0.0 we have user-defined functions and map-valued variables, again on about the same complexity level as ``awk`` along with optional type-declaration syntax; as of Miller 6 we have full support for arrays. While I'm excited by these powerful language features, I hope to keep new features focused on Miller's sweet spot which is speed plus simplicity.
|
||||
I do want to keep focusing on what Miller is good at -- concise notation, low latency, and high throughput -- and not add too much in terms of high-level-language features to the DSL. That said, some sort of customizability is a basic thing to want. As of 4.1.0 we have recursive for/while/if structures on about the same complexity level as `awk`; as of 5.0.0 we have user-defined functions and map-valued variables, again on about the same complexity level as `awk` along with optional type-declaration syntax; as of Miller 6 we have full support for arrays. While I'm excited by these powerful language features, I hope to keep new features focused on Miller's sweet spot which is speed plus simplicity.
|
||||
|
||||
|
|
|
|||
|
|
@ -3,10 +3,12 @@
|
|||
|
||||
## Pattern-action blocks
|
||||
|
||||
These are reminiscent of ``awk`` syntax. They can be used to allow assignments to be done only when appropriate -- e.g. for math-function domain restrictions, regex-matching, and so on:
|
||||
These are reminiscent of `awk` syntax. They can be used to allow assignments to be done only when appropriate -- e.g. for math-function domain restrictions, regex-matching, and so on:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr cat data/put-gating-example-1.dkvp</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
x=-1
|
||||
x=0
|
||||
x=1
|
||||
|
|
@ -14,8 +16,10 @@ x=2
|
|||
x=3
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put '$x > 0.0 { $y = log10($x); $z = sqrt($y) }' data/put-gating-example-1.dkvp</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
x=-1
|
||||
x=0
|
||||
x=1,y=0,z=0
|
||||
|
|
@ -23,58 +27,66 @@ x=2,y=0.3010299956639812,z=0.5486620049392715
|
|||
x=3,y=0.4771212547196624,z=0.6907396432228734
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr cat data/put-gating-example-2.dkvp</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=abc_123
|
||||
a=some other name
|
||||
a=xyz_789
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put '</b>
|
||||
<b> $a =~ "([a-z]+)_([0-9]+)" {</b>
|
||||
<b> $b = "left_\1"; $c = "right_\2"</b>
|
||||
<b> }' \</b>
|
||||
<b> data/put-gating-example-2.dkvp</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=abc_123,b=left_\1,c=right_\2
|
||||
a=some other name
|
||||
a=xyz_789,b=left_\1,c=right_\2
|
||||
</pre>
|
||||
|
||||
This produces heteregenous output which Miller, of course, has no problems with (see [Record Heterogeneity](record-heterogeneity.md)). But if you want homogeneous output, the curly braces can be replaced with a semicolon between the expression and the body statements. This causes ``put`` to evaluate the boolean expression (along with any side effects, namely, regex-captures ``\1``, ``\2``, etc.) but doesn't use it as a criterion for whether subsequent assignments should be executed. Instead, subsequent assignments are done unconditionally:
|
||||
This produces heteregenous output which Miller, of course, has no problems with (see [Record Heterogeneity](record-heterogeneity.md)). But if you want homogeneous output, the curly braces can be replaced with a semicolon between the expression and the body statements. This causes `put` to evaluate the boolean expression (along with any side effects, namely, regex-captures `\1`, `\2`, etc.) but doesn't use it as a criterion for whether subsequent assignments should be executed. Instead, subsequent assignments are done unconditionally:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put '$x > 0.0; $y = log10($x); $z = sqrt($y)' data/put-gating-example-1.dkvp</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
x=1,y=0,z=0
|
||||
x=2,y=0.3010299956639812,z=0.5486620049392715
|
||||
x=3,y=0.4771212547196624,z=0.6907396432228734
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put '</b>
|
||||
<b> $a =~ "([a-z]+)_([0-9]+)";</b>
|
||||
<b> $b = "left_\1";</b>
|
||||
<b> $c = "right_\2"</b>
|
||||
<b>' data/put-gating-example-2.dkvp</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=abc_123,b=left_\1,c=right_\2
|
||||
a=xyz_789,b=left_\1,c=right_\2
|
||||
</pre>
|
||||
|
||||
## If-statements
|
||||
|
||||
These are again reminiscent of ``awk``. Pattern-action blocks are a special case of ``if`` with no ``elif`` or ``else`` blocks, no ``if`` keyword, and parentheses optional around the boolean expression:
|
||||
These are again reminiscent of `awk`. Pattern-action blocks are a special case of `if` with no `elif` or `else` blocks, no `if` keyword, and parentheses optional around the boolean expression:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put 'NR == 4 {$foo = "bar"}'</b>
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put 'if (NR == 4) {$foo = "bar"}'</b>
|
||||
</pre>
|
||||
|
||||
Compound statements use ``elif`` (rather than ``elsif`` or ``else if``):
|
||||
Compound statements use `elif` (rather than `elsif` or `else if`):
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
mlr put '
|
||||
if (NR == 2) {
|
||||
...
|
||||
|
|
@ -90,19 +102,21 @@ mlr put '
|
|||
|
||||
## While and do-while loops
|
||||
|
||||
Miller's ``while`` and ``do-while`` are unsurprising in comparison to various languages, as are ``break`` and ``continue``:
|
||||
Miller's `while` and `do-while` are unsurprising in comparison to various languages, as are `break` and `continue`:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>echo x=1,y=2 | mlr put '</b>
|
||||
<b> while (NF < 10) {</b>
|
||||
<b> $[NF+1] = ""</b>
|
||||
<b> }</b>
|
||||
<b> $foo = "bar"</b>
|
||||
<b>'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
x=1,y=2,3=,4=,5=,6=,7=,8=,9=,10=,foo=bar
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>echo x=1,y=2 | mlr put '</b>
|
||||
<b> do {</b>
|
||||
<b> $[NF+1] = "";</b>
|
||||
|
|
@ -112,24 +126,26 @@ x=1,y=2,3=,4=,5=,6=,7=,8=,9=,10=,foo=bar
|
|||
<b> } while (NF < 10);</b>
|
||||
<b> $foo = "bar"</b>
|
||||
<b>'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
x=1,y=2,3=,4=,5=,foo=bar
|
||||
</pre>
|
||||
|
||||
A ``break`` or ``continue`` within nested conditional blocks or if-statements will, of course, propagate to the innermost loop enclosing them, if any. A ``break`` or ``continue`` outside a loop is a syntax error that will be flagged as soon as the expression is parsed, before any input records are ingested.
|
||||
The existence of ``while``, ``do-while``, and ``for`` loops in Miller's DSL means that you can create infinite-loop scenarios inadvertently. In particular, please recall that DSL statements are executed once if in ``begin`` or ``end`` blocks, and once *per record* otherwise. For example, **while (NR < 10) will never terminate as NR is only incremented between records**.
|
||||
A `break` or `continue` within nested conditional blocks or if-statements will, of course, propagate to the innermost loop enclosing them, if any. A `break` or `continue` outside a loop is a syntax error that will be flagged as soon as the expression is parsed, before any input records are ingested.
|
||||
The existence of `while`, `do-while`, and `for` loops in Miller's DSL means that you can create infinite-loop scenarios inadvertently. In particular, please recall that DSL statements are executed once if in `begin` or `end` blocks, and once *per record* otherwise. For example, **while (NR < 10) will never terminate as NR is only incremented between records**.
|
||||
|
||||
## For-loops
|
||||
|
||||
While Miller's ``while`` and ``do-while`` statements are much as in many other languages, ``for`` loops are more idiosyncratic to Miller. They are loops over key-value pairs, whether in stream records, out-of-stream variables, local variables, or map-literals: more reminiscent of ``foreach``, as in (for example) PHP. There are **for-loops over map keys** and **for-loops over key-value tuples**. Additionally, Miller has a **C-style triple-for loop** with initialize, test, and update statements.
|
||||
While Miller's `while` and `do-while` statements are much as in many other languages, `for` loops are more idiosyncratic to Miller. They are loops over key-value pairs, whether in stream records, out-of-stream variables, local variables, or map-literals: more reminiscent of `foreach`, as in (for example) PHP. There are **for-loops over map keys** and **for-loops over key-value tuples**. Additionally, Miller has a **C-style triple-for loop** with initialize, test, and update statements.
|
||||
|
||||
As with ``while`` and ``do-while``, a ``break`` or ``continue`` within nested control structures will propagate to the innermost loop enclosing them, if any, and a ``break`` or ``continue`` outside a loop is a syntax error that will be flagged as soon as the expression is parsed, before any input records are ingested.
|
||||
As with `while` and `do-while`, a `break` or `continue` within nested control structures will propagate to the innermost loop enclosing them, if any, and a `break` or `continue` outside a loop is a syntax error that will be flagged as soon as the expression is parsed, before any input records are ingested.
|
||||
|
||||
Key-only for-loops
|
||||
................................................................
|
||||
|
||||
The ``key`` variable is always bound to the *key* of key-value pairs:
|
||||
The `key` variable is always bound to the *key* of key-value pairs:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from data/small put '</b>
|
||||
<b> print "NR = ".NR;</b>
|
||||
<b> for (key in $*) {</b>
|
||||
|
|
@ -138,6 +154,8 @@ The ``key`` variable is always bound to the *key* of key-value pairs:
|
|||
<b> }</b>
|
||||
<b></b>
|
||||
<b>'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
NR = 1
|
||||
key:a value:pan
|
||||
key:b value:pan
|
||||
|
|
@ -175,7 +193,7 @@ NR = 5
|
|||
a=wye,b=pan,i=5,x=0.5732889198020006,y=0.8636244699032729
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr -n put '</b>
|
||||
<b> end {</b>
|
||||
<b> o = {1:2, 3:{4:5}};</b>
|
||||
|
|
@ -184,26 +202,30 @@ a=wye,b=pan,i=5,x=0.5732889198020006,y=0.8636244699032729
|
|||
<b> }</b>
|
||||
<b> }</b>
|
||||
<b>'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
key:1 valuetype:int
|
||||
key:3 valuetype:map
|
||||
</pre>
|
||||
|
||||
Note that the value corresponding to a given key may be gotten as through a **computed field name** using square brackets as in ``$[key]`` for stream records, or by indexing the looped-over variable using square brackets.
|
||||
Note that the value corresponding to a given key may be gotten as through a **computed field name** using square brackets as in `$[key]` for stream records, or by indexing the looped-over variable using square brackets.
|
||||
|
||||
Key-value for-loops
|
||||
................................................................
|
||||
|
||||
Single-level keys may be gotten at using either ``for(k,v)`` or ``for((k),v)``; multi-level keys may be gotten at using ``for((k1,k2,k3),v)`` and so on. The ``v`` variable will be bound to to a scalar value (a string or a number) if the map stops at that level, or to a map-valued variable if the map goes deeper. If the map isn't deep enough then the loop body won't be executed.
|
||||
Single-level keys may be gotten at using either `for(k,v)` or `for((k),v)`; multi-level keys may be gotten at using `for((k1,k2,k3),v)` and so on. The `v` variable will be bound to to a scalar value (a string or a number) if the map stops at that level, or to a map-valued variable if the map goes deeper. If the map isn't deep enough then the loop body won't be executed.
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/for-srec-example.tbl</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
label1 label2 f1 f2 f3
|
||||
blue green 100 240 350
|
||||
red green 120 11 195
|
||||
yellow blue 140 0 240
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --pprint --from data/for-srec-example.tbl put '</b>
|
||||
<b> $sum1 = $f1 + $f2 + $f3;</b>
|
||||
<b> $sum2 = 0;</b>
|
||||
|
|
@ -215,14 +237,18 @@ yellow blue 140 0 240
|
|||
<b> }</b>
|
||||
<b> }</b>
|
||||
<b>'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
label1 label2 f1 f2 f3 sum1 sum2 sum3
|
||||
blue green 100 240 350 690 690 690
|
||||
red green 120 11 195 326 326 326
|
||||
yellow blue 140 0 240 380 380 380
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from data/small --opprint put 'for (k,v in $*) { $[k."_type"] = typeof(v) }'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a b i x y a_type b_type i_type x_type y_type
|
||||
pan pan 1 0.3467901443380824 0.7268028627434533 string string int float float
|
||||
eks pan 2 0.7586799647899636 0.5221511083334797 string string int float float
|
||||
|
|
@ -231,11 +257,11 @@ eks wye 4 0.38139939387114097 0.13418874328430463 string string int float fl
|
|||
wye pan 5 0.5732889198020006 0.8636244699032729 string string int float float
|
||||
</pre>
|
||||
|
||||
Note that the value of the current field in the for-loop can be gotten either using the bound variable ``value``, or through a **computed field name** using square brackets as in ``$[key]``.
|
||||
Note that the value of the current field in the for-loop can be gotten either using the bound variable `value`, or through a **computed field name** using square brackets as in `$[key]`.
|
||||
|
||||
Important note: to avoid inconsistent looping behavior in case you're setting new fields (and/or unsetting existing ones) while looping over the record, **Miller makes a copy of the record before the loop: loop variables are bound from the copy and all other reads/writes involve the record itself**:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from data/small --opprint put '</b>
|
||||
<b> $sum1 = 0;</b>
|
||||
<b> $sum2 = 0;</b>
|
||||
|
|
@ -246,6 +272,8 @@ Important note: to avoid inconsistent looping behavior in case you're setting ne
|
|||
<b> }</b>
|
||||
<b> }</b>
|
||||
<b>'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a b i x y sum1 sum2
|
||||
pan pan 1 0.3467901443380824 0.7268028627434533 2.0735930070815356 8.294372028326142
|
||||
eks pan 2 0.7586799647899636 0.5221511083334797 3.280831073123443 13.123324292493772
|
||||
|
|
@ -256,7 +284,7 @@ wye pan 5 0.5732889198020006 0.8636244699032729 6.436913389705273 25.74765355
|
|||
|
||||
It can be confusing to modify the stream record while iterating over a copy of it, so instead you might find it simpler to use a local variable in the loop and only update the stream record after the loop:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from data/small --opprint put '</b>
|
||||
<b> sum = 0;</b>
|
||||
<b> for (k,v in $*) {</b>
|
||||
|
|
@ -266,6 +294,8 @@ It can be confusing to modify the stream record while iterating over a copy of i
|
|||
<b> }</b>
|
||||
<b> $sum = sum</b>
|
||||
<b>'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a b i x y sum
|
||||
pan pan 1 0.3467901443380824 0.7268028627434533 2.0735930070815356
|
||||
eks pan 2 0.7586799647899636 0.5221511083334797 3.280831073123443
|
||||
|
|
@ -274,9 +304,9 @@ eks wye 4 0.38139939387114097 0.13418874328430463 4.515588137155445
|
|||
wye pan 5 0.5732889198020006 0.8636244699032729 6.436913389705273
|
||||
</pre>
|
||||
|
||||
You can also start iterating on sub-hashmaps of an out-of-stream or local variable; you can loop over nested keys; you can loop over all out-of-stream variables. The bound variables are bound to a copy of the sub-hashmap as it was before the loop started. The sub-hashmap is specified by square-bracketed indices after ``in``, and additional deeper indices are bound to loop key-variables. The terminal values are bound to the loop value-variable whenever the keys are not too shallow. The value-variable may refer to a terminal (string, number) or it may be map-valued if the map goes deeper. Example indexing is as follows:
|
||||
You can also start iterating on sub-hashmaps of an out-of-stream or local variable; you can loop over nested keys; you can loop over all out-of-stream variables. The bound variables are bound to a copy of the sub-hashmap as it was before the loop started. The sub-hashmap is specified by square-bracketed indices after `in`, and additional deeper indices are bound to loop key-variables. The terminal values are bound to the loop value-variable whenever the keys are not too shallow. The value-variable may refer to a terminal (string, number) or it may be map-valued if the map goes deeper. Example indexing is as follows:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
# Parentheses are optional for single key:
|
||||
for (k1, v in @a["b"]["c"]) { ... }
|
||||
for ((k1), v in @a["b"]["c"]) { ... }
|
||||
|
|
@ -287,9 +317,9 @@ for ((k1, k2, k3), v in @a { ... } # Loop over variable starting from
|
|||
for ((k1, k2, k3), v in @* { ... } # Loop over all variables (k1 is bound to basename)
|
||||
</pre>
|
||||
|
||||
That's confusing in the abstract, so a concrete example is in order. Suppose the out-of-stream variable ``@myvar`` is populated as follows:
|
||||
That's confusing in the abstract, so a concrete example is in order. Suppose the out-of-stream variable `@myvar` is populated as follows:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr -n put --jknquoteint -q '</b>
|
||||
<b> begin {</b>
|
||||
<b> @myvar = {</b>
|
||||
|
|
@ -300,6 +330,8 @@ That's confusing in the abstract, so a concrete example is in order. Suppose the
|
|||
<b> }</b>
|
||||
<b> end { dump }</b>
|
||||
<b>'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
{
|
||||
"myvar": {
|
||||
"1": 2,
|
||||
|
|
@ -317,7 +349,7 @@ That's confusing in the abstract, so a concrete example is in order. Suppose the
|
|||
|
||||
Then we can get at various values as follows:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr -n put --jknquoteint -q '</b>
|
||||
<b> begin {</b>
|
||||
<b> @myvar = {</b>
|
||||
|
|
@ -334,12 +366,14 @@ Then we can get at various values as follows:
|
|||
<b> }</b>
|
||||
<b> }</b>
|
||||
<b>'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
key=1,valuetype=int
|
||||
key=3,valuetype=map
|
||||
key=6,valuetype=map
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr -n put --jknquoteint -q '</b>
|
||||
<b> begin {</b>
|
||||
<b> @myvar = {</b>
|
||||
|
|
@ -357,11 +391,13 @@ key=6,valuetype=map
|
|||
<b> }</b>
|
||||
<b> }</b>
|
||||
<b>'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
key1=3,key2=4,valuetype=int
|
||||
key1=6,key2=7,valuetype=map
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr -n put --jknquoteint -q '</b>
|
||||
<b> begin {</b>
|
||||
<b> @myvar = {</b>
|
||||
|
|
@ -379,6 +415,8 @@ key1=6,key2=7,valuetype=map
|
|||
<b> }</b>
|
||||
<b> }</b>
|
||||
<b>'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
key1=7,key2=8,valuetype=int
|
||||
</pre>
|
||||
|
||||
|
|
@ -387,7 +425,7 @@ C-style triple-for loops
|
|||
|
||||
These are supported as follows:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from data/small --opprint put '</b>
|
||||
<b> num suma = 0;</b>
|
||||
<b> for (a = 1; a <= NR; a += 1) {</b>
|
||||
|
|
@ -395,6 +433,8 @@ These are supported as follows:
|
|||
<b> }</b>
|
||||
<b> $suma = suma;</b>
|
||||
<b>'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a b i x y suma
|
||||
pan pan 1 0.3467901443380824 0.7268028627434533 1
|
||||
eks pan 2 0.7586799647899636 0.5221511083334797 3
|
||||
|
|
@ -403,7 +443,7 @@ eks wye 4 0.38139939387114097 0.13418874328430463 10
|
|||
wye pan 5 0.5732889198020006 0.8636244699032729 15
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from data/small --opprint put '</b>
|
||||
<b> num suma = 0;</b>
|
||||
<b> num sumb = 0;</b>
|
||||
|
|
@ -414,6 +454,8 @@ wye pan 5 0.5732889198020006 0.8636244699032729 15
|
|||
<b> $suma = suma;</b>
|
||||
<b> $sumb = sumb;</b>
|
||||
<b>'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a b i x y suma sumb
|
||||
pan pan 1 0.3467901443380824 0.7268028627434533 1 1
|
||||
eks pan 2 0.7586799647899636 0.5221511083334797 3 3
|
||||
|
|
@ -424,26 +466,28 @@ wye pan 5 0.5732889198020006 0.8636244699032729 15 31
|
|||
|
||||
Notes:
|
||||
|
||||
* In ``for (start; continuation; update) { body }``, the start, continuation, and update statements may be empty, single statements, or multiple comma-separated statements. If the continuation is empty (e.g. ``for(i=1;;i+=1)``) it defaults to true.
|
||||
* In `for (start; continuation; update) { body }`, the start, continuation, and update statements may be empty, single statements, or multiple comma-separated statements. If the continuation is empty (e.g. `for(i=1;;i+=1)`) it defaults to true.
|
||||
|
||||
* In particular, you may use ``$``-variables and/or ``@``-variables in the start, continuation, and/or update steps (as well as the body, of course).
|
||||
* In particular, you may use `$`-variables and/or `@`-variables in the start, continuation, and/or update steps (as well as the body, of course).
|
||||
|
||||
* The typedecls such as ``int`` or ``num`` are optional. If a typedecl is provided (for a local variable), it binds a variable scoped to the for-loop regardless of whether a same-name variable is present in outer scope. If a typedecl is not provided, then the variable is scoped to the for-loop if no same-name variable is present in outer scope, or if a same-name variable is present in outer scope then it is modified.
|
||||
* The typedecls such as `int` or `num` are optional. If a typedecl is provided (for a local variable), it binds a variable scoped to the for-loop regardless of whether a same-name variable is present in outer scope. If a typedecl is not provided, then the variable is scoped to the for-loop if no same-name variable is present in outer scope, or if a same-name variable is present in outer scope then it is modified.
|
||||
|
||||
* Miller has no ``++`` or ``--`` operators.
|
||||
* Miller has no `++` or `--` operators.
|
||||
|
||||
* As with all for/if/while statements in Miller, the curly braces are required even if the body is a single statement, or empty.
|
||||
|
||||
## Begin/end blocks
|
||||
|
||||
Miller supports an ``awk``-like ``begin/end`` syntax. The statements in the ``begin`` block are executed before any input records are read; the statements in the ``end`` block are executed after the last input record is read. (If you want to execute some statement at the start of each file, not at the start of the first file as with ``begin``, you might use a pattern/action block of the form ``FNR == 1 { ... }``.) All statements outside of ``begin`` or ``end`` are, of course, executed on every input record. Semicolons separate statements inside or outside of begin/end blocks; semicolons are required between begin/end block bodies and any subsequent statement. For example:
|
||||
Miller supports an `awk`-like `begin/end` syntax. The statements in the `begin` block are executed before any input records are read; the statements in the `end` block are executed after the last input record is read. (If you want to execute some statement at the start of each file, not at the start of the first file as with `begin`, you might use a pattern/action block of the form `FNR == 1 { ... }`.) All statements outside of `begin` or `end` are, of course, executed on every input record. Semicolons separate statements inside or outside of begin/end blocks; semicolons are required between begin/end block bodies and any subsequent statement. For example:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put '</b>
|
||||
<b> begin { @sum = 0 };</b>
|
||||
<b> @x_sum += $x;</b>
|
||||
<b> end { emit @x_sum }</b>
|
||||
<b>' ./data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,b=pan,i=1,x=0.3467901443380824,y=0.7268028627434533
|
||||
a=eks,b=pan,i=2,x=0.7586799647899636,y=0.5221511083334797
|
||||
a=wye,b=wye,i=3,x=0.20460330576630303,y=0.33831852551664776
|
||||
|
|
@ -452,13 +496,15 @@ a=wye,b=pan,i=5,x=0.5732889198020006,y=0.8636244699032729
|
|||
x_sum=2.264761728567491
|
||||
</pre>
|
||||
|
||||
Since uninitialized out-of-stream variables default to 0 for addition/substraction and 1 for multiplication when they appear on expression right-hand sides (not quite as in ``awk``, where they'd default to 0 either way), the above can be written more succinctly as
|
||||
Since uninitialized out-of-stream variables default to 0 for addition/substraction and 1 for multiplication when they appear on expression right-hand sides (not quite as in `awk`, where they'd default to 0 either way), the above can be written more succinctly as
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put '</b>
|
||||
<b> @x_sum += $x;</b>
|
||||
<b> end { emit @x_sum }</b>
|
||||
<b>' ./data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,b=pan,i=1,x=0.3467901443380824,y=0.7268028627434533
|
||||
a=eks,b=pan,i=2,x=0.7586799647899636,y=0.5221511083334797
|
||||
a=wye,b=wye,i=3,x=0.20460330576630303,y=0.33831852551664776
|
||||
|
|
@ -467,19 +513,21 @@ a=wye,b=pan,i=5,x=0.5732889198020006,y=0.8636244699032729
|
|||
x_sum=2.264761728567491
|
||||
</pre>
|
||||
|
||||
The **put -q** option is a shorthand which suppresses printing of each output record, with only ``emit`` statements being output. So to get only summary outputs, one could write
|
||||
The **put -q** option is a shorthand which suppresses printing of each output record, with only `emit` statements being output. So to get only summary outputs, one could write
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put -q '</b>
|
||||
<b> @x_sum += $x;</b>
|
||||
<b> end { emit @x_sum }</b>
|
||||
<b>' ./data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
x_sum=2.264761728567491
|
||||
</pre>
|
||||
|
||||
We can do similarly with multiple out-of-stream variables:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put -q '</b>
|
||||
<b> @x_count += 1;</b>
|
||||
<b> @x_sum += $x;</b>
|
||||
|
|
@ -488,16 +536,20 @@ We can do similarly with multiple out-of-stream variables:
|
|||
<b> emit @x_sum;</b>
|
||||
<b> }</b>
|
||||
<b>' ./data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
x_count=5
|
||||
x_sum=2.264761728567491
|
||||
</pre>
|
||||
|
||||
This is of course not much different than
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr stats1 -a count,sum -f x ./data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
x_count=5,x_sum=2.264761728567491
|
||||
</pre>
|
||||
|
||||
Note that it's a syntax error for begin/end blocks to refer to field names (beginning with ``$``), since these execute outside the context of input records.
|
||||
Note that it's a syntax error for begin/end blocks to refer to field names (beginning with `$`), since these execute outside the context of input records.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
## Pattern-action blocks
|
||||
|
||||
These are reminiscent of ``awk`` syntax. They can be used to allow assignments to be done only when appropriate -- e.g. for math-function domain restrictions, regex-matching, and so on:
|
||||
These are reminiscent of `awk` syntax. They can be used to allow assignments to be done only when appropriate -- e.g. for math-function domain restrictions, regex-matching, and so on:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr cat data/put-gating-example-1.dkvp
|
||||
|
|
@ -24,7 +24,7 @@ mlr put '
|
|||
data/put-gating-example-2.dkvp
|
||||
GENMD_EOF
|
||||
|
||||
This produces heteregenous output which Miller, of course, has no problems with (see [Record Heterogeneity](record-heterogeneity.md)). But if you want homogeneous output, the curly braces can be replaced with a semicolon between the expression and the body statements. This causes ``put`` to evaluate the boolean expression (along with any side effects, namely, regex-captures ``\1``, ``\2``, etc.) but doesn't use it as a criterion for whether subsequent assignments should be executed. Instead, subsequent assignments are done unconditionally:
|
||||
This produces heteregenous output which Miller, of course, has no problems with (see [Record Heterogeneity](record-heterogeneity.md)). But if you want homogeneous output, the curly braces can be replaced with a semicolon between the expression and the body statements. This causes `put` to evaluate the boolean expression (along with any side effects, namely, regex-captures `\1`, `\2`, etc.) but doesn't use it as a criterion for whether subsequent assignments should be executed. Instead, subsequent assignments are done unconditionally:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr put '$x > 0.0; $y = log10($x); $z = sqrt($y)' data/put-gating-example-1.dkvp
|
||||
|
|
@ -40,7 +40,7 @@ GENMD_EOF
|
|||
|
||||
## If-statements
|
||||
|
||||
These are again reminiscent of ``awk``. Pattern-action blocks are a special case of ``if`` with no ``elif`` or ``else`` blocks, no ``if`` keyword, and parentheses optional around the boolean expression:
|
||||
These are again reminiscent of `awk`. Pattern-action blocks are a special case of `if` with no `elif` or `else` blocks, no `if` keyword, and parentheses optional around the boolean expression:
|
||||
|
||||
GENMD_SHOW_COMMAND
|
||||
mlr put 'NR == 4 {$foo = "bar"}'
|
||||
|
|
@ -50,42 +50,40 @@ GENMD_SHOW_COMMAND
|
|||
mlr put 'if (NR == 4) {$foo = "bar"}'
|
||||
GENMD_EOF
|
||||
|
||||
Compound statements use ``elif`` (rather than ``elsif`` or ``else if``):
|
||||
Compound statements use `elif` (rather than `elsif` or `else if`):
|
||||
|
||||
GENMD_INCLUDE_ESCAPED(data/if-chain.sh)
|
||||
|
||||
## While and do-while loops
|
||||
|
||||
Miller's ``while`` and ``do-while`` are unsurprising in comparison to various languages, as are ``break`` and ``continue``:
|
||||
Miller's `while` and `do-while` are unsurprising in comparison to various languages, as are `break` and `continue`:
|
||||
|
||||
GENMD_INCLUDE_AND_RUN_ESCAPED(data/while-example-1.sh)
|
||||
|
||||
GENMD_INCLUDE_AND_RUN_ESCAPED(data/while-example-2.sh)
|
||||
|
||||
A ``break`` or ``continue`` within nested conditional blocks or if-statements will, of course, propagate to the innermost loop enclosing them, if any. A ``break`` or ``continue`` outside a loop is a syntax error that will be flagged as soon as the expression is parsed, before any input records are ingested.
|
||||
The existence of ``while``, ``do-while``, and ``for`` loops in Miller's DSL means that you can create infinite-loop scenarios inadvertently. In particular, please recall that DSL statements are executed once if in ``begin`` or ``end`` blocks, and once *per record* otherwise. For example, **while (NR < 10) will never terminate as NR is only incremented between records**.
|
||||
A `break` or `continue` within nested conditional blocks or if-statements will, of course, propagate to the innermost loop enclosing them, if any. A `break` or `continue` outside a loop is a syntax error that will be flagged as soon as the expression is parsed, before any input records are ingested.
|
||||
The existence of `while`, `do-while`, and `for` loops in Miller's DSL means that you can create infinite-loop scenarios inadvertently. In particular, please recall that DSL statements are executed once if in `begin` or `end` blocks, and once *per record* otherwise. For example, **while (NR < 10) will never terminate as NR is only incremented between records**.
|
||||
|
||||
## For-loops
|
||||
|
||||
While Miller's ``while`` and ``do-while`` statements are much as in many other languages, ``for`` loops are more idiosyncratic to Miller. They are loops over key-value pairs, whether in stream records, out-of-stream variables, local variables, or map-literals: more reminiscent of ``foreach``, as in (for example) PHP. There are **for-loops over map keys** and **for-loops over key-value tuples**. Additionally, Miller has a **C-style triple-for loop** with initialize, test, and update statements.
|
||||
While Miller's `while` and `do-while` statements are much as in many other languages, `for` loops are more idiosyncratic to Miller. They are loops over key-value pairs, whether in stream records, out-of-stream variables, local variables, or map-literals: more reminiscent of `foreach`, as in (for example) PHP. There are **for-loops over map keys** and **for-loops over key-value tuples**. Additionally, Miller has a **C-style triple-for loop** with initialize, test, and update statements.
|
||||
|
||||
As with ``while`` and ``do-while``, a ``break`` or ``continue`` within nested control structures will propagate to the innermost loop enclosing them, if any, and a ``break`` or ``continue`` outside a loop is a syntax error that will be flagged as soon as the expression is parsed, before any input records are ingested.
|
||||
As with `while` and `do-while`, a `break` or `continue` within nested control structures will propagate to the innermost loop enclosing them, if any, and a `break` or `continue` outside a loop is a syntax error that will be flagged as soon as the expression is parsed, before any input records are ingested.
|
||||
|
||||
Key-only for-loops
|
||||
................................................................
|
||||
### Key-only for-loops
|
||||
|
||||
The ``key`` variable is always bound to the *key* of key-value pairs:
|
||||
The `key` variable is always bound to the *key* of key-value pairs:
|
||||
|
||||
GENMD_INCLUDE_AND_RUN_ESCAPED(data/single-for-example-1.sh)
|
||||
|
||||
GENMD_INCLUDE_AND_RUN_ESCAPED(data/single-for-example-2.sh)
|
||||
|
||||
Note that the value corresponding to a given key may be gotten as through a **computed field name** using square brackets as in ``$[key]`` for stream records, or by indexing the looped-over variable using square brackets.
|
||||
Note that the value corresponding to a given key may be gotten as through a **computed field name** using square brackets as in `$[key]` for stream records, or by indexing the looped-over variable using square brackets.
|
||||
|
||||
Key-value for-loops
|
||||
................................................................
|
||||
### Key-value for-loops
|
||||
|
||||
Single-level keys may be gotten at using either ``for(k,v)`` or ``for((k),v)``; multi-level keys may be gotten at using ``for((k1,k2,k3),v)`` and so on. The ``v`` variable will be bound to to a scalar value (a string or a number) if the map stops at that level, or to a map-valued variable if the map goes deeper. If the map isn't deep enough then the loop body won't be executed.
|
||||
Single-level keys may be gotten at using either `for(k,v)` or `for((k),v)`; multi-level keys may be gotten at using `for((k1,k2,k3),v)` and so on. The `v` variable will be bound to to a scalar value (a string or a number) if the map stops at that level, or to a map-valued variable if the map goes deeper. If the map isn't deep enough then the loop body won't be executed.
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
cat data/for-srec-example.tbl
|
||||
|
|
@ -97,7 +95,7 @@ GENMD_RUN_COMMAND
|
|||
mlr --from data/small --opprint put 'for (k,v in $*) { $[k."_type"] = typeof(v) }'
|
||||
GENMD_EOF
|
||||
|
||||
Note that the value of the current field in the for-loop can be gotten either using the bound variable ``value``, or through a **computed field name** using square brackets as in ``$[key]``.
|
||||
Note that the value of the current field in the for-loop can be gotten either using the bound variable `value`, or through a **computed field name** using square brackets as in `$[key]`.
|
||||
|
||||
Important note: to avoid inconsistent looping behavior in case you're setting new fields (and/or unsetting existing ones) while looping over the record, **Miller makes a copy of the record before the loop: loop variables are bound from the copy and all other reads/writes involve the record itself**:
|
||||
|
||||
|
|
@ -107,11 +105,11 @@ It can be confusing to modify the stream record while iterating over a copy of i
|
|||
|
||||
GENMD_INCLUDE_AND_RUN_ESCAPED(data/for-srec-example-3.sh)
|
||||
|
||||
You can also start iterating on sub-hashmaps of an out-of-stream or local variable; you can loop over nested keys; you can loop over all out-of-stream variables. The bound variables are bound to a copy of the sub-hashmap as it was before the loop started. The sub-hashmap is specified by square-bracketed indices after ``in``, and additional deeper indices are bound to loop key-variables. The terminal values are bound to the loop value-variable whenever the keys are not too shallow. The value-variable may refer to a terminal (string, number) or it may be map-valued if the map goes deeper. Example indexing is as follows:
|
||||
You can also start iterating on sub-hashmaps of an out-of-stream or local variable; you can loop over nested keys; you can loop over all out-of-stream variables. The bound variables are bound to a copy of the sub-hashmap as it was before the loop started. The sub-hashmap is specified by square-bracketed indices after `in`, and additional deeper indices are bound to loop key-variables. The terminal values are bound to the loop value-variable whenever the keys are not too shallow. The value-variable may refer to a terminal (string, number) or it may be map-valued if the map goes deeper. Example indexing is as follows:
|
||||
|
||||
GENMD_INCLUDE_ESCAPED(data/for-oosvar-example-0a.txt)
|
||||
|
||||
That's confusing in the abstract, so a concrete example is in order. Suppose the out-of-stream variable ``@myvar`` is populated as follows:
|
||||
That's confusing in the abstract, so a concrete example is in order. Suppose the out-of-stream variable `@myvar` is populated as follows:
|
||||
|
||||
GENMD_INCLUDE_AND_RUN_ESCAPED(data/for-oosvar-example-0b.sh)
|
||||
|
||||
|
|
@ -123,8 +121,7 @@ GENMD_INCLUDE_AND_RUN_ESCAPED(data/for-oosvar-example-0d.sh)
|
|||
|
||||
GENMD_INCLUDE_AND_RUN_ESCAPED(data/for-oosvar-example-0e.sh)
|
||||
|
||||
C-style triple-for loops
|
||||
................................................................
|
||||
### C-style triple-for loops
|
||||
|
||||
These are supported as follows:
|
||||
|
||||
|
|
@ -134,27 +131,27 @@ GENMD_INCLUDE_AND_RUN_ESCAPED(data/triple-for-example-2.sh)
|
|||
|
||||
Notes:
|
||||
|
||||
* In ``for (start; continuation; update) { body }``, the start, continuation, and update statements may be empty, single statements, or multiple comma-separated statements. If the continuation is empty (e.g. ``for(i=1;;i+=1)``) it defaults to true.
|
||||
* In `for (start; continuation; update) { body }`, the start, continuation, and update statements may be empty, single statements, or multiple comma-separated statements. If the continuation is empty (e.g. `for(i=1;;i+=1)`) it defaults to true.
|
||||
|
||||
* In particular, you may use ``$``-variables and/or ``@``-variables in the start, continuation, and/or update steps (as well as the body, of course).
|
||||
* In particular, you may use `$`-variables and/or `@`-variables in the start, continuation, and/or update steps (as well as the body, of course).
|
||||
|
||||
* The typedecls such as ``int`` or ``num`` are optional. If a typedecl is provided (for a local variable), it binds a variable scoped to the for-loop regardless of whether a same-name variable is present in outer scope. If a typedecl is not provided, then the variable is scoped to the for-loop if no same-name variable is present in outer scope, or if a same-name variable is present in outer scope then it is modified.
|
||||
* The typedecls such as `int` or `num` are optional. If a typedecl is provided (for a local variable), it binds a variable scoped to the for-loop regardless of whether a same-name variable is present in outer scope. If a typedecl is not provided, then the variable is scoped to the for-loop if no same-name variable is present in outer scope, or if a same-name variable is present in outer scope then it is modified.
|
||||
|
||||
* Miller has no ``++`` or ``--`` operators.
|
||||
* Miller has no `++` or `--` operators.
|
||||
|
||||
* As with all for/if/while statements in Miller, the curly braces are required even if the body is a single statement, or empty.
|
||||
|
||||
## Begin/end blocks
|
||||
|
||||
Miller supports an ``awk``-like ``begin/end`` syntax. The statements in the ``begin`` block are executed before any input records are read; the statements in the ``end`` block are executed after the last input record is read. (If you want to execute some statement at the start of each file, not at the start of the first file as with ``begin``, you might use a pattern/action block of the form ``FNR == 1 { ... }``.) All statements outside of ``begin`` or ``end`` are, of course, executed on every input record. Semicolons separate statements inside or outside of begin/end blocks; semicolons are required between begin/end block bodies and any subsequent statement. For example:
|
||||
Miller supports an `awk`-like `begin/end` syntax. The statements in the `begin` block are executed before any input records are read; the statements in the `end` block are executed after the last input record is read. (If you want to execute some statement at the start of each file, not at the start of the first file as with `begin`, you might use a pattern/action block of the form `FNR == 1 { ... }`.) All statements outside of `begin` or `end` are, of course, executed on every input record. Semicolons separate statements inside or outside of begin/end blocks; semicolons are required between begin/end block bodies and any subsequent statement. For example:
|
||||
|
||||
GENMD_INCLUDE_AND_RUN_ESCAPED(data/begin-end-example-1.sh)
|
||||
|
||||
Since uninitialized out-of-stream variables default to 0 for addition/substraction and 1 for multiplication when they appear on expression right-hand sides (not quite as in ``awk``, where they'd default to 0 either way), the above can be written more succinctly as
|
||||
Since uninitialized out-of-stream variables default to 0 for addition/substraction and 1 for multiplication when they appear on expression right-hand sides (not quite as in `awk`, where they'd default to 0 either way), the above can be written more succinctly as
|
||||
|
||||
GENMD_INCLUDE_AND_RUN_ESCAPED(data/begin-end-example-2.sh)
|
||||
|
||||
The **put -q** option is a shorthand which suppresses printing of each output record, with only ``emit`` statements being output. So to get only summary outputs, one could write
|
||||
The **put -q** option is a shorthand which suppresses printing of each output record, with only `emit` statements being output. So to get only summary outputs, one could write
|
||||
|
||||
GENMD_INCLUDE_AND_RUN_ESCAPED(data/begin-end-example-3.sh)
|
||||
|
||||
|
|
@ -166,5 +163,5 @@ This is of course not much different than
|
|||
|
||||
GENMD_INCLUDE_AND_RUN_ESCAPED(data/begin-end-example-5.sh)
|
||||
|
||||
Note that it's a syntax error for begin/end blocks to refer to field names (beginning with ``$``), since these execute outside the context of input records.
|
||||
Note that it's a syntax error for begin/end blocks to refer to field names (beginning with `$`), since these execute outside the context of input records.
|
||||
|
||||
|
|
|
|||
|
|
@ -3,22 +3,22 @@
|
|||
|
||||
As soon as you have a programming language, you start having the problem *What is my code doing, and why?* This includes getting syntax errors -- which are always annoying -- as well as the even more annoying problem of a program which parses without syntax error but doesn't do what you expect.
|
||||
|
||||
The ``syntax error`` message is cryptic: it says ``syntax error at `` followed by the next symbol it couldn't parse. This is good, but (as of 5.0.0) it doesn't say things like ``syntax error at line 17, character 22``. Here are some common causes of syntax errors:
|
||||
The `syntax error` message is cryptic: it says `syntax error at ` followed by the next symbol it couldn't parse. This is good, but (as of 5.0.0) it doesn't say things like `syntax error at line 17, character 22`. Here are some common causes of syntax errors:
|
||||
|
||||
* Don't forget ``;`` at end of line, before another statement on the next line.
|
||||
* Don't forget `;` at end of line, before another statement on the next line.
|
||||
|
||||
* Miller's DSL lacks the ``++`` and ``--`` operators.
|
||||
* Miller's DSL lacks the `++` and `--` operators.
|
||||
|
||||
* Curly braces are required for the bodies of ``if``/``while``/``for`` blocks, even when the body is a single statement.
|
||||
* Curly braces are required for the bodies of `if`/`while`/`for` blocks, even when the body is a single statement.
|
||||
|
||||
Now for transparency:
|
||||
As for transparency:
|
||||
|
||||
* As in any language, you can do (see :ref:`reference-dsl-print-statements`) ``print`` (or ``eprint`` to print to stderr). See also :ref:`reference-dsl-dump-statements` and :ref:`reference-dsl-emit-statements`.
|
||||
* As in any language, you can do `print`, or `eprint` to print to stderr. See [Print statements](reference-dsl-output-statements.md#print-statements); see also [Dump statements](reference-dsl-output-statements.md#dump-statements) and [Emit statements](reference-dsl-output-statements.md#emit-statements).
|
||||
|
||||
* The ``-v`` option to ``mlr put`` and ``mlr filter`` prints abstract syntax trees for your code. While not all details here will be of interest to everyone, certainly this makes questions such as operator precedence completely unambiguous.
|
||||
* The `-v` option to `mlr put` and `mlr filter` prints abstract syntax trees for your code. While not all details here will be of interest to everyone, certainly this makes questions such as operator precedence completely unambiguous.
|
||||
|
||||
* The ``-T`` option prints a trace of each statement executed.
|
||||
* The `-T` option prints a trace of each statement executed.
|
||||
|
||||
* The ``-t`` and ``-a`` options show low-level details for the parsing process and for stack-variable-index allocation, respectively. These will likely be of interest to people who enjoy compilers, and probably less useful for a more general audience.
|
||||
* The `-t` and `-a` options show low-level details for the parsing process and for stack-variable-index allocation, respectively. These will likely be of interest to people who enjoy compilers, and probably less useful for a more general audience.
|
||||
|
||||
* Please see :ref:`reference-dsl-type-checking` for type declarations and type-assertions you can use to make sure expressions and the data flowing them are evaluating as you expect. I made them optional because one of Miller's important use-cases is being able to say simple things like ``mlr put '$y = $x + 1' myfile.dat`` with a minimum of punctuational bric-a-brac -- but for programs over a few lines I generally find that the more type-specification, the better.
|
||||
* Please see [type-checking](reference-dsl-variables.md#type-checking) for type declarations and type-assertions you can use to make sure expressions and the data flowing them are evaluating as you expect. I made them optional because one of Miller's important use-cases is being able to say simple things like `mlr put '$y = $x + 1' myfile.dat` with a minimum of punctuational bric-a-brac -- but for programs over a few lines I generally find that the more type-specification, the better.
|
||||
|
|
|
|||
|
|
@ -2,22 +2,22 @@
|
|||
|
||||
As soon as you have a programming language, you start having the problem *What is my code doing, and why?* This includes getting syntax errors -- which are always annoying -- as well as the even more annoying problem of a program which parses without syntax error but doesn't do what you expect.
|
||||
|
||||
The ``syntax error`` message is cryptic: it says ``syntax error at `` followed by the next symbol it couldn't parse. This is good, but (as of 5.0.0) it doesn't say things like ``syntax error at line 17, character 22``. Here are some common causes of syntax errors:
|
||||
The `syntax error` message is cryptic: it says `syntax error at ` followed by the next symbol it couldn't parse. This is good, but (as of 5.0.0) it doesn't say things like `syntax error at line 17, character 22`. Here are some common causes of syntax errors:
|
||||
|
||||
* Don't forget ``;`` at end of line, before another statement on the next line.
|
||||
* Don't forget `;` at end of line, before another statement on the next line.
|
||||
|
||||
* Miller's DSL lacks the ``++`` and ``--`` operators.
|
||||
* Miller's DSL lacks the `++` and `--` operators.
|
||||
|
||||
* Curly braces are required for the bodies of ``if``/``while``/``for`` blocks, even when the body is a single statement.
|
||||
* Curly braces are required for the bodies of `if`/`while`/`for` blocks, even when the body is a single statement.
|
||||
|
||||
Now for transparency:
|
||||
As for transparency:
|
||||
|
||||
* As in any language, you can do (see :ref:`reference-dsl-print-statements`) ``print`` (or ``eprint`` to print to stderr). See also :ref:`reference-dsl-dump-statements` and :ref:`reference-dsl-emit-statements`.
|
||||
* As in any language, you can do `print`, or `eprint` to print to stderr. See [Print statements](reference-dsl-output-statements.md#print-statements); see also [Dump statements](reference-dsl-output-statements.md#dump-statements) and [Emit statements](reference-dsl-output-statements.md#emit-statements).
|
||||
|
||||
* The ``-v`` option to ``mlr put`` and ``mlr filter`` prints abstract syntax trees for your code. While not all details here will be of interest to everyone, certainly this makes questions such as operator precedence completely unambiguous.
|
||||
* The `-v` option to `mlr put` and `mlr filter` prints abstract syntax trees for your code. While not all details here will be of interest to everyone, certainly this makes questions such as operator precedence completely unambiguous.
|
||||
|
||||
* The ``-T`` option prints a trace of each statement executed.
|
||||
* The `-T` option prints a trace of each statement executed.
|
||||
|
||||
* The ``-t`` and ``-a`` options show low-level details for the parsing process and for stack-variable-index allocation, respectively. These will likely be of interest to people who enjoy compilers, and probably less useful for a more general audience.
|
||||
* The `-t` and `-a` options show low-level details for the parsing process and for stack-variable-index allocation, respectively. These will likely be of interest to people who enjoy compilers, and probably less useful for a more general audience.
|
||||
|
||||
* Please see :ref:`reference-dsl-type-checking` for type declarations and type-assertions you can use to make sure expressions and the data flowing them are evaluating as you expect. I made them optional because one of Miller's important use-cases is being able to say simple things like ``mlr put '$y = $x + 1' myfile.dat`` with a minimum of punctuational bric-a-brac -- but for programs over a few lines I generally find that the more type-specification, the better.
|
||||
* Please see [type-checking](reference-dsl-variables.md#type-checking) for type declarations and type-assertions you can use to make sure expressions and the data flowing them are evaluating as you expect. I made them optional because one of Miller's important use-cases is being able to say simple things like `mlr put '$y = $x + 1' myfile.dat` with a minimum of punctuational bric-a-brac -- but for programs over a few lines I generally find that the more type-specification, the better.
|
||||
|
|
|
|||
|
|
@ -1,31 +1,39 @@
|
|||
<!--- PLEASE DO NOT EDIT DIRECTLY. EDIT THE .md.in FILE PLEASE. --->
|
||||
# DSL reference: filter statements
|
||||
|
||||
You can use ``filter`` within ``put``. In fact, the following two are synonymous:
|
||||
You can use `filter` within `put`. In fact, the following two are synonymous:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr filter 'NR==2 || NR==3' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=eks,b=pan,i=2,x=0.7586799647899636,y=0.5221511083334797
|
||||
a=wye,b=wye,i=3,x=0.20460330576630303,y=0.33831852551664776
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put 'filter NR==2 || NR==3' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=eks,b=pan,i=2,x=0.7586799647899636,y=0.5221511083334797
|
||||
a=wye,b=wye,i=3,x=0.20460330576630303,y=0.33831852551664776
|
||||
</pre>
|
||||
|
||||
The former, of course, is much easier to type. But the latter allows you to define more complex expressions for the filter, and/or do other things in addition to the filter:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put '@running_sum += $x; filter @running_sum > 1.3' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=wye,b=wye,i=3,x=0.20460330576630303,y=0.33831852551664776
|
||||
a=eks,b=wye,i=4,x=0.38139939387114097,y=0.13418874328430463
|
||||
a=wye,b=pan,i=5,x=0.5732889198020006,y=0.8636244699032729
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put '$z = $x * $y; filter $z > 0.3' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=eks,b=pan,i=2,x=0.7586799647899636,y=0.5221511083334797,z=0.3961455844854848
|
||||
a=wye,b=pan,i=5,x=0.5732889198020006,y=0.8636244699032729,z=0.4951063394654227
|
||||
</pre>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# DSL reference: filter statements
|
||||
|
||||
You can use ``filter`` within ``put``. In fact, the following two are synonymous:
|
||||
You can use `filter` within `put`. In fact, the following two are synonymous:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr filter 'NR==2 || NR==3' data/small
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
Operators are listed in order of decreasing precedence, highest first.
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
Operators Associativity
|
||||
--------- -------------
|
||||
() left to right
|
||||
|
|
@ -30,11 +30,11 @@ binary+ binary- . left to right
|
|||
|
||||
* Functions are often pass-throughs straight to the system-standard Go libraries.
|
||||
|
||||
* The ``min`` and ``max`` functions are different from other multi-argument functions which return null if any of their inputs are null: for ``min`` and ``max``, by contrast, if one argument is absent-null, the other is returned. Empty-null loses min or max against numeric or boolean; empty-null is less than any other string.
|
||||
* The `min` and `max` functions are different from other multi-argument functions which return null if any of their inputs are null: for `min` and `max`, by contrast, if one argument is absent-null, the other is returned. Empty-null loses min or max against numeric or boolean; empty-null is less than any other string.
|
||||
|
||||
* Symmetrically with respect to the bitwise OR, XOR, and AND operators ``|``, ``^``, ``&``, Miller has logical operators ``||``, ``^^``, ``&&``: the logical XOR not existing in Go.
|
||||
* Symmetrically with respect to the bitwise OR, XOR, and AND operators `|`, `^`, `&`, Miller has logical operators `||`, `^^`, `&&`: the logical XOR not existing in Go.
|
||||
|
||||
* The exponentiation operator ``**`` is familiar from many languages.
|
||||
* The exponentiation operator `**` is familiar from many languages.
|
||||
|
||||
* The regex-match and regex-not-match operators ``=~`` and ``!=~`` are similar to those in Ruby and Perl.
|
||||
* The regex-match and regex-not-match operators `=~` and `!=~` are similar to those in Ruby and Perl.
|
||||
|
||||
|
|
|
|||
|
|
@ -29,11 +29,11 @@ GENMD_EOF
|
|||
|
||||
* Functions are often pass-throughs straight to the system-standard Go libraries.
|
||||
|
||||
* The ``min`` and ``max`` functions are different from other multi-argument functions which return null if any of their inputs are null: for ``min`` and ``max``, by contrast, if one argument is absent-null, the other is returned. Empty-null loses min or max against numeric or boolean; empty-null is less than any other string.
|
||||
* The `min` and `max` functions are different from other multi-argument functions which return null if any of their inputs are null: for `min` and `max`, by contrast, if one argument is absent-null, the other is returned. Empty-null loses min or max against numeric or boolean; empty-null is less than any other string.
|
||||
|
||||
* Symmetrically with respect to the bitwise OR, XOR, and AND operators ``|``, ``^``, ``&``, Miller has logical operators ``||``, ``^^``, ``&&``: the logical XOR not existing in Go.
|
||||
* Symmetrically with respect to the bitwise OR, XOR, and AND operators `|`, `^`, `&`, Miller has logical operators `||`, `^^`, `&&`: the logical XOR not existing in Go.
|
||||
|
||||
* The exponentiation operator ``**`` is familiar from many languages.
|
||||
* The exponentiation operator `**` is familiar from many languages.
|
||||
|
||||
* The regex-match and regex-not-match operators ``=~`` and ``!=~`` are similar to those in Ruby and Perl.
|
||||
* The regex-match and regex-not-match operators `=~` and `!=~` are similar to those in Ruby and Perl.
|
||||
|
||||
|
|
|
|||
|
|
@ -3,63 +3,57 @@
|
|||
|
||||
You can **output** variable-values or expressions in **five ways**:
|
||||
|
||||
* **Assign** them to stream-record fields. For example, ``$cumulative_sum = @sum``. For another example, ``$nr = NR`` adds a field named ``nr`` to each output record, containing the value of the built-in variable ``NR`` as of when that record was ingested.
|
||||
* **Assign** them to stream-record fields. For example, `$cumulative_sum = @sum`. For another example, `$nr = NR` adds a field named `nr` to each output record, containing the value of the built-in variable `NR` as of when that record was ingested.
|
||||
|
||||
* Use the **print** or **eprint** keywords which immediately print an expression *directly to standard output or standard error*, respectively. Note that ``dump``, ``edump``, ``print``, and ``eprint`` don't output records which participate in ``then``-chaining; rather, they're just immediate prints to stdout/stderr. The ``printn`` and ``eprintn`` keywords are the same except that they don't print final newlines. Additionally, you can print to a specified file instead of stdout/stderr.
|
||||
* Use the **print** or **eprint** keywords which immediately print an expression *directly to standard output or standard error*, respectively. Note that `dump`, `edump`, `print`, and `eprint` don't output records which participate in `then`-chaining; rather, they're just immediate prints to stdout/stderr. The `printn` and `eprintn` keywords are the same except that they don't print final newlines. Additionally, you can print to a specified file instead of stdout/stderr.
|
||||
|
||||
* Use the **dump** or **edump** keywords, which *immediately print all out-of-stream variables as a JSON data structure to the standard output or standard error* (respectively).
|
||||
|
||||
* Use **tee** which formats the current stream record (not just an arbitrary string as with **print**) to a specific file.
|
||||
|
||||
* Use **emit**/**emitp**/**emitf** to send out-of-stream variables' current values to the output record stream, e.g. ``@sum += $x; emit @sum`` which produces an extra output record such as ``sum=3.1648382``.
|
||||
* Use **emit**/**emitp**/**emitf** to send out-of-stream variables' current values to the output record stream, e.g. `@sum += $x; emit @sum` which produces an extra output record such as `sum=3.1648382`.
|
||||
|
||||
For the first two options you are populating the output-records stream which feeds into the next verb in a ``then``-chain (if any), or which otherwise is formatted for output using ``--o...`` flags.
|
||||
For the first two options you are populating the output-records stream which feeds into the next verb in a `then`-chain (if any), or which otherwise is formatted for output using `--o...` flags.
|
||||
|
||||
For the last three options you are sending output directly to standard output, standard error, or a file.
|
||||
|
||||
.. _reference-dsl-print-statements:
|
||||
|
||||
## Print statements
|
||||
|
||||
The ``print`` statement is perhaps self-explanatory, but with a few light caveats:
|
||||
The `print` statement is perhaps self-explanatory, but with a few light caveats:
|
||||
|
||||
* There are four variants: ``print`` goes to stdout with final newline, ``printn`` goes to stdout without final newline (you can include one using "\n" in your output string), ``eprint`` goes to stderr with final newline, and ``eprintn`` goes to stderr without final newline.
|
||||
* There are four variants: `print` goes to stdout with final newline, `printn` goes to stdout without final newline (you can include one using "\n" in your output string), `eprint` goes to stderr with final newline, and `eprintn` goes to stderr without final newline.
|
||||
|
||||
* Output goes directly to stdout/stderr, respectively: data produced this way do not go downstream to the next verb in a ``then``-chain. (Use ``emit`` for that.)
|
||||
* Output goes directly to stdout/stderr, respectively: data produced this way do not go downstream to the next verb in a `then`-chain. (Use `emit` for that.)
|
||||
|
||||
* Print statements are for strings (``print "hello"``), or things which can be made into strings: numbers (``print 3``, ``print $a + $b``, or concatenations thereof (``print "a + b = " . ($a + $b)``). Maps (in ``$*``, map-valued out-of-stream or local variables, and map literals) aren't convertible into strings. If you print a map, you get ``{is-a-map}`` as output. Please use ``dump`` to print maps.
|
||||
* Print statements are for strings (`print "hello"`), or things which can be made into strings: numbers (`print 3`, `print $a + $b`, or concatenations thereof (`print "a + b = " . ($a + $b)`). Maps (in `$*`, map-valued out-of-stream or local variables, and map literals) aren't convertible into strings. If you print a map, you get `{is-a-map}` as output. Please use `dump` to print maps.
|
||||
|
||||
* You can redirect print output to a file: ``mlr --from myfile.dat put 'print > "tap.txt", $x'`` ``mlr --from myfile.dat put 'o=$*; print > $a.".txt", $x'``.
|
||||
* You can redirect print output to a file: `mlr --from myfile.dat put 'print > "tap.txt", $x'` `mlr --from myfile.dat put 'o=$*; print > $a.".txt", $x'`.
|
||||
|
||||
* See also :ref:`reference-dsl-redirected-output-statements` for examples.
|
||||
|
||||
.. _reference-dsl-dump-statements:
|
||||
* See also [Redirected-output statements](reference-dsl-output-statements.md#redirected-output-statements) for examples.
|
||||
|
||||
## Dump statements
|
||||
|
||||
The ``dump`` statement is for printing expressions, including maps, directly to stdout/stderr, respectively:
|
||||
The `dump` statement is for printing expressions, including maps, directly to stdout/stderr, respectively:
|
||||
|
||||
* There are two variants: ``dump`` prints to stdout; ``edump`` prints to stderr.
|
||||
* There are two variants: `dump` prints to stdout; `edump` prints to stderr.
|
||||
|
||||
* Output goes directly to stdout/stderr, respectively: data produced this way do not go downstream to the next verb in a ``then``-chain. (Use ``emit`` for that.)
|
||||
* Output goes directly to stdout/stderr, respectively: data produced this way do not go downstream to the next verb in a `then`-chain. (Use `emit` for that.)
|
||||
|
||||
* You can use ``dump`` to output single strings, numbers, or expressions including map-valued data. Map-valued data are printed as JSON. Miller allows string and integer keys in its map literals while JSON allows only string keys, so use ``mlr put --jknquoteint`` if you want integer-valued map keys not double-quoted.
|
||||
* You can use `dump` to output single strings, numbers, or expressions including map-valued data. Map-valued data are printed as JSON. Miller allows string and integer keys in its map literals while JSON allows only string keys, so use `mlr put --jknquoteint` if you want integer-valued map keys not double-quoted.
|
||||
|
||||
* If you use ``dump`` (or ``edump``) with no arguments, you get a JSON structure representing the current values of all out-of-stream variables.
|
||||
* If you use `dump` (or `edump`) with no arguments, you get a JSON structure representing the current values of all out-of-stream variables.
|
||||
|
||||
* As with ``print``, you can redirect output to files.
|
||||
* As with `print`, you can redirect output to files.
|
||||
|
||||
* See also :ref:`reference-dsl-redirected-output-statements` for examples.
|
||||
* See also [Redirected-output statements](reference-dsl-output-statements.md#redirected-output-statements) for examples.
|
||||
|
||||
## Tee statements
|
||||
|
||||
Records produced by a ``mlr put`` go downstream to the next verb in your ``then``-chain, if any, or otherwise to standard output. If you want to additionally copy out records to files, you can do that using ``tee``.
|
||||
Records produced by a `mlr put` go downstream to the next verb in your `then`-chain, if any, or otherwise to standard output. If you want to additionally copy out records to files, you can do that using `tee`.
|
||||
|
||||
The syntax is, by example, ``mlr --from myfile.dat put 'tee > "tap.dat", $*' then sort -n index``. First is ``tee >``, then the filename expression (which can be an expression such as ``"tap.".$a.".dat"``), then a comma, then ``$*``. (Nothing else but ``$*`` is teeable.)
|
||||
The syntax is, by example, `mlr --from myfile.dat put 'tee > "tap.dat", $*' then sort -n index`. First is `tee >`, then the filename expression (which can be an expression such as `"tap.".$a.".dat"`), then a comma, then `$*`. (Nothing else but `$*` is teeable.)
|
||||
|
||||
See also :ref:`reference-dsl-redirected-output-statements` for examples.
|
||||
|
||||
.. _reference-dsl-redirected-output-statements:
|
||||
See also [Redirected-output statements](reference-dsl-output-statements.md#redirected-output-statements) for examples.
|
||||
|
||||
## Redirected-output statements
|
||||
|
||||
|
|
@ -67,10 +61,12 @@ The **print**, **dump** **tee**, **emitf**, **emit**, and **emitp** keywords all
|
|||
|
||||
Details:
|
||||
|
||||
* The ``print`` and ``dump`` keywords produce output immediately to standard output, or to specified file(s) or pipe-to command if present.
|
||||
* The `print` and `dump` keywords produce output immediately to standard output, or to specified file(s) or pipe-to command if present.
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr help keyword print</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
print: prints expression immediately to stdout.
|
||||
|
||||
Example: mlr --from f.dat put -q 'print "The sum of x and y is ".($x+$y)'
|
||||
|
|
@ -78,8 +74,10 @@ print: prints expression immediately to stdout.
|
|||
Example: mlr --from f.dat put '(NR %% 1000 == 0) { print > stderr, "Checkpoint ".NR}'
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr help keyword dump</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
dump: prints all currently defined out-of-stream variables immediately
|
||||
to stdout as JSON.
|
||||
|
||||
|
|
@ -99,10 +97,12 @@ the main command line.
|
|||
Example: mlr --from f.dat put -q '@v[NR]=$*; end { dump | "jq .[]"}'
|
||||
</pre>
|
||||
|
||||
* ``mlr put`` sends the current record (possibly modified by the ``put`` expression) to the output record stream. Records are then input to the following verb in a ``then``-chain (if any), else printed to standard output (unless ``put -q``). The **tee** keyword *additionally* writes the output record to specified file(s) or pipe-to command, or immediately to ``stdout``/``stderr``.
|
||||
* `mlr put` sends the current record (possibly modified by the `put` expression) to the output record stream. Records are then input to the following verb in a `then`-chain (if any), else printed to standard output (unless `put -q`). The **tee** keyword *additionally* writes the output record to specified file(s) or pipe-to command, or immediately to `stdout`/`stderr`.
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr help keyword tee</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
tee: prints the current record to specified file.
|
||||
This is an immediate print to the specified file (except for pprint format
|
||||
which of course waits until the end of the input stream to format all output).
|
||||
|
|
@ -129,10 +129,12 @@ output $*.
|
|||
Example: mlr --from f.dat put -q --ojson 'tee | "gzip > /tmp/data-".$a.".gz", $*'
|
||||
</pre>
|
||||
|
||||
* ``mlr put``'s ``emitf``, ``emitp``, and ``emit`` send out-of-stream variables to the output record stream. These are then input to the following verb in a ``then``-chain (if any), else printed to standard output. When redirected with ``>``, ``>>``, or ``|``, they *instead* write the out-of-stream variable(s) to specified file(s) or pipe-to command, or immediately to ``stdout``/``stderr``.
|
||||
* `mlr put`'s `emitf`, `emitp`, and `emit` send out-of-stream variables to the output record stream. These are then input to the following verb in a `then`-chain (if any), else printed to standard output. When redirected with `>`, `>>`, or `|`, they *instead* write the out-of-stream variable(s) to specified file(s) or pipe-to command, or immediately to `stdout`/`stderr`.
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr help keyword emitf</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
emitf: inserts non-indexed out-of-stream variable(s) side-by-side into the
|
||||
output record stream.
|
||||
|
||||
|
|
@ -161,8 +163,10 @@ etc., to control the format of the output if the output is redirected. See also
|
|||
Please see https://johnkerl.org/miller6://johnkerl.org/miller/doc for more information.
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr help keyword emitp</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
emitp: inserts an out-of-stream variable into the output record stream.
|
||||
Hashmap indices present in the data but not slotted by emitp arguments are
|
||||
output concatenated with ":".
|
||||
|
|
@ -193,8 +197,10 @@ etc., to control the format of the output if the output is redirected. See also
|
|||
Please see https://johnkerl.org/miller6://johnkerl.org/miller/doc for more information.
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr help keyword emit</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
emit: inserts an out-of-stream variable into the output record stream. Hashmap
|
||||
indices present in the data but not slotted by emit arguments are not output.
|
||||
|
||||
|
|
@ -226,28 +232,30 @@ etc., to control the format of the output if the output is redirected. See also
|
|||
Please see https://johnkerl.org/miller6://johnkerl.org/miller/doc for more information.
|
||||
</pre>
|
||||
|
||||
.. _reference-dsl-emit-statements:
|
||||
|
||||
## Emit statements
|
||||
|
||||
There are three variants: ``emitf``, ``emit``, and ``emitp``. Keep in mind that out-of-stream variables are a nested, multi-level hashmap (directly viewable as JSON using ``dump``), whereas Miller output records are lists of single-level key-value pairs. The three emit variants allow you to control how the multilevel hashmaps are flatten down to output records. You can emit any map-valued expression, including ``$*``, map-valued out-of-stream variables, the entire out-of-stream-variable collection ``@*``, map-valued local variables, map literals, or map-valued function return values.
|
||||
There are three variants: `emitf`, `emit`, and `emitp`. Keep in mind that out-of-stream variables are a nested, multi-level hashmap (directly viewable as JSON using `dump`), whereas Miller output records are lists of single-level key-value pairs. The three emit variants allow you to control how the multilevel hashmaps are flatten down to output records. You can emit any map-valued expression, including `$*`, map-valued out-of-stream variables, the entire out-of-stream-variable collection `@*`, map-valued local variables, map literals, or map-valued function return values.
|
||||
|
||||
Use **emitf** to output several out-of-stream variables side-by-side in the same output record. For ``emitf`` these mustn't have indexing using ``@name[...]``. Example:
|
||||
Use **emitf** to output several out-of-stream variables side-by-side in the same output record. For `emitf` these mustn't have indexing using `@name[...]`. Example:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put -q '</b>
|
||||
<b> @count += 1;</b>
|
||||
<b> @x_sum += $x;</b>
|
||||
<b> @y_sum += $y;</b>
|
||||
<b> end { emitf @count, @x_sum, @y_sum}</b>
|
||||
<b>' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
count=5,x_sum=2.264761728567491,y_sum=2.585085709781158
|
||||
</pre>
|
||||
|
||||
Use **emit** to output an out-of-stream variable. If it's non-indexed you'll get a simple key-value pair:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,b=pan,i=1,x=0.3467901443380824,y=0.7268028627434533
|
||||
a=eks,b=pan,i=2,x=0.7586799647899636,y=0.5221511083334797
|
||||
a=wye,b=wye,i=3,x=0.20460330576630303,y=0.33831852551664776
|
||||
|
|
@ -255,22 +263,28 @@ a=eks,b=wye,i=4,x=0.38139939387114097,y=0.13418874328430463
|
|||
a=wye,b=pan,i=5,x=0.5732889198020006,y=0.8636244699032729
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put -q '@sum += $x; end { dump }' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
{
|
||||
"sum": 2.264761728567491
|
||||
}
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put -q '@sum += $x; end { emit @sum }' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
sum=2.264761728567491
|
||||
</pre>
|
||||
|
||||
If it's indexed then use as many names after ``emit`` as there are indices:
|
||||
If it's indexed then use as many names after `emit` as there are indices:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put -q '@sum[$a] += $x; end { dump }' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
{
|
||||
"sum": {
|
||||
"pan": 0.3467901443380824,
|
||||
|
|
@ -280,15 +294,19 @@ If it's indexed then use as many names after ``emit`` as there are indices:
|
|||
}
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put -q '@sum[$a] += $x; end { emit @sum, "a" }' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,sum=0.3467901443380824
|
||||
a=eks,sum=1.1400793586611044
|
||||
a=wye,sum=0.7778922255683036
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put -q '@sum[$a][$b] += $x; end { dump }' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
{
|
||||
"sum": {
|
||||
"pan": {
|
||||
|
|
@ -306,8 +324,10 @@ a=wye,sum=0.7778922255683036
|
|||
}
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put -q '@sum[$a][$b] += $x; end { emit @sum, "a", "b" }' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,b=pan,sum=0.3467901443380824
|
||||
a=eks,b=pan,sum=0.7586799647899636
|
||||
a=eks,b=wye,sum=0.38139939387114097
|
||||
|
|
@ -315,8 +335,10 @@ a=wye,b=wye,sum=0.20460330576630303
|
|||
a=wye,b=pan,sum=0.5732889198020006
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put -q '@sum[$a][$b][$i] += $x; end { dump }' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
{
|
||||
"sum": {
|
||||
"pan": {
|
||||
|
|
@ -344,11 +366,13 @@ a=wye,b=pan,sum=0.5732889198020006
|
|||
}
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put -q '</b>
|
||||
<b> @sum[$a][$b][$i] += $x;</b>
|
||||
<b> end { emit @sum, "a", "b", "i" }</b>
|
||||
<b>' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,b=pan,i=1,sum=0.3467901443380824
|
||||
a=eks,b=pan,i=2,sum=0.7586799647899636
|
||||
a=eks,b=wye,i=4,sum=0.38139939387114097
|
||||
|
|
@ -356,10 +380,12 @@ a=wye,b=wye,i=3,sum=0.20460330576630303
|
|||
a=wye,b=pan,i=5,sum=0.5732889198020006
|
||||
</pre>
|
||||
|
||||
Now for **emitp**: if you have as many names following ``emit`` as there are levels in the out-of-stream variable's hashmap, then ``emit`` and ``emitp`` do the same thing. Where they differ is when you don't specify as many names as there are hashmap levels. In this case, Miller needs to flatten multiple map indices down to output-record keys: ``emitp`` includes full prefixing (hence the ``p`` in ``emitp``) while ``emit`` takes the deepest hashmap key as the output-record key:
|
||||
Now for **emitp**: if you have as many names following `emit` as there are levels in the out-of-stream variable's hashmap, then `emit` and `emitp` do the same thing. Where they differ is when you don't specify as many names as there are hashmap levels. In this case, Miller needs to flatten multiple map indices down to output-record keys: `emitp` includes full prefixing (hence the `p` in `emitp`) while `emit` takes the deepest hashmap key as the output-record key:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put -q '@sum[$a][$b] += $x; end { dump }' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
{
|
||||
"sum": {
|
||||
"pan": {
|
||||
|
|
@ -377,34 +403,44 @@ Now for **emitp**: if you have as many names following ``emit`` as there are lev
|
|||
}
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put -q '@sum[$a][$b] += $x; end { emit @sum, "a" }' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,pan=0.3467901443380824
|
||||
a=eks,pan=0.7586799647899636,wye=0.38139939387114097
|
||||
a=wye,wye=0.20460330576630303,pan=0.5732889198020006
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put -q '@sum[$a][$b] += $x; end { emit @sum }' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
pan=0.3467901443380824
|
||||
pan=0.7586799647899636,wye=0.38139939387114097
|
||||
wye=0.20460330576630303,pan=0.5732889198020006
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put -q '@sum[$a][$b] += $x; end { emitp @sum, "a" }' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,sum.pan=0.3467901443380824
|
||||
a=eks,sum.pan=0.7586799647899636,sum.wye=0.38139939387114097
|
||||
a=wye,sum.wye=0.20460330576630303,sum.pan=0.5732889198020006
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put -q '@sum[$a][$b] += $x; end { emitp @sum }' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
sum.pan.pan=0.3467901443380824,sum.eks.pan=0.7586799647899636,sum.eks.wye=0.38139939387114097,sum.wye.wye=0.20460330576630303,sum.wye.pan=0.5732889198020006
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --oxtab put -q '@sum[$a][$b] += $x; end { emitp @sum }' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
sum.pan.pan 0.3467901443380824
|
||||
sum.eks.pan 0.7586799647899636
|
||||
sum.eks.wye 0.38139939387114097
|
||||
|
|
@ -413,25 +449,31 @@ sum.wye.pan 0.5732889198020006
|
|||
</pre>
|
||||
|
||||
Use **--oflatsep** to specify the character which joins multilevel
|
||||
keys for ``emitp`` (it defaults to a colon):
|
||||
keys for `emitp` (it defaults to a colon):
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put -q --oflatsep / '@sum[$a][$b] += $x; end { emitp @sum, "a" }' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,sum.pan=0.3467901443380824
|
||||
a=eks,sum.pan=0.7586799647899636,sum.wye=0.38139939387114097
|
||||
a=wye,sum.wye=0.20460330576630303,sum.pan=0.5732889198020006
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put -q --oflatsep / '@sum[$a][$b] += $x; end { emitp @sum }' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
sum.pan.pan=0.3467901443380824,sum.eks.pan=0.7586799647899636,sum.eks.wye=0.38139939387114097,sum.wye.wye=0.20460330576630303,sum.wye.pan=0.5732889198020006
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --oxtab put -q --oflatsep / '</b>
|
||||
<b> @sum[$a][$b] += $x;</b>
|
||||
<b> end { emitp @sum }</b>
|
||||
<b>' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
sum.pan.pan 0.3467901443380824
|
||||
sum.eks.pan 0.7586799647899636
|
||||
sum.eks.wye 0.38139939387114097
|
||||
|
|
@ -444,7 +486,7 @@ sum.wye.pan 0.5732889198020006
|
|||
You can emit **multiple map-valued expressions side-by-side** by
|
||||
including their names in parentheses:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from data/medium --opprint put -q '</b>
|
||||
<b> @x_count[$a][$b] += 1;</b>
|
||||
<b> @x_sum[$a][$b] += $x;</b>
|
||||
|
|
@ -455,6 +497,8 @@ including their names in parentheses:
|
|||
<b> emit (@x_sum, @x_count, @x_mean), "a", "b"</b>
|
||||
<b> }</b>
|
||||
<b>'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a b x_sum x_count x_mean
|
||||
pan pan 219.1851288316854 427 0.5133141190437597
|
||||
pan wye 198.43293070748447 395 0.5023618498923658
|
||||
|
|
@ -483,18 +527,20 @@ hat hat 182.8535323148762 381 0.47993053101017374
|
|||
hat pan 168.5538067327806 363 0.4643355557376876
|
||||
</pre>
|
||||
|
||||
What this does is walk through the first out-of-stream variable (``@x_sum`` in this example) as usual, then for each keylist found (e.g. ``pan,wye``), include the values for the remaining out-of-stream variables (here, ``@x_count`` and ``@x_mean``). You should use this when all out-of-stream variables in the emit statement have **the same shape and the same keylists**.
|
||||
What this does is walk through the first out-of-stream variable (`@x_sum` in this example) as usual, then for each keylist found (e.g. `pan,wye`), include the values for the remaining out-of-stream variables (here, `@x_count` and `@x_mean`). You should use this when all out-of-stream variables in the emit statement have **the same shape and the same keylists**.
|
||||
|
||||
## Emit-all statements
|
||||
|
||||
Use **emit all** (or ``emit @*`` which is synonymous) to output all out-of-stream variables. You can use the following idiom to get various accumulators output side-by-side (reminiscent of ``mlr stats1``):
|
||||
Use **emit all** (or `emit @*` which is synonymous) to output all out-of-stream variables. You can use the following idiom to get various accumulators output side-by-side (reminiscent of `mlr stats1`):
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from data/small --opprint put -q '</b>
|
||||
<b> @v[$a][$b]["sum"] += $x;</b>
|
||||
<b> @v[$a][$b]["count"] += 1;</b>
|
||||
<b> end{emit @*,"a","b"}</b>
|
||||
<b>'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a b v.sum v.count
|
||||
pan pan 0.3467901443380824 1
|
||||
eks pan 0.7586799647899636 1
|
||||
|
|
@ -503,12 +549,14 @@ wye wye 0.20460330576630303 1
|
|||
wye pan 0.5732889198020006 1
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from data/small --opprint put -q '</b>
|
||||
<b> @sum[$a][$b] += $x;</b>
|
||||
<b> @count[$a][$b] += 1;</b>
|
||||
<b> end{emit @*,"a","b"}</b>
|
||||
<b>'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a b sum
|
||||
pan pan 0.3467901443380824
|
||||
eks pan 0.7586799647899636
|
||||
|
|
@ -524,12 +572,14 @@ wye wye 1
|
|||
wye pan 1
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from data/small --opprint put -q '</b>
|
||||
<b> @sum[$a][$b] += $x;</b>
|
||||
<b> @count[$a][$b] += 1;</b>
|
||||
<b> end{emit (@sum, @count),"a","b"}</b>
|
||||
<b>'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a b sum count
|
||||
pan pan 0.3467901443380824 1
|
||||
eks pan 0.7586799647899636 1
|
||||
|
|
|
|||
|
|
@ -2,63 +2,57 @@
|
|||
|
||||
You can **output** variable-values or expressions in **five ways**:
|
||||
|
||||
* **Assign** them to stream-record fields. For example, ``$cumulative_sum = @sum``. For another example, ``$nr = NR`` adds a field named ``nr`` to each output record, containing the value of the built-in variable ``NR`` as of when that record was ingested.
|
||||
* **Assign** them to stream-record fields. For example, `$cumulative_sum = @sum`. For another example, `$nr = NR` adds a field named `nr` to each output record, containing the value of the built-in variable `NR` as of when that record was ingested.
|
||||
|
||||
* Use the **print** or **eprint** keywords which immediately print an expression *directly to standard output or standard error*, respectively. Note that ``dump``, ``edump``, ``print``, and ``eprint`` don't output records which participate in ``then``-chaining; rather, they're just immediate prints to stdout/stderr. The ``printn`` and ``eprintn`` keywords are the same except that they don't print final newlines. Additionally, you can print to a specified file instead of stdout/stderr.
|
||||
* Use the **print** or **eprint** keywords which immediately print an expression *directly to standard output or standard error*, respectively. Note that `dump`, `edump`, `print`, and `eprint` don't output records which participate in `then`-chaining; rather, they're just immediate prints to stdout/stderr. The `printn` and `eprintn` keywords are the same except that they don't print final newlines. Additionally, you can print to a specified file instead of stdout/stderr.
|
||||
|
||||
* Use the **dump** or **edump** keywords, which *immediately print all out-of-stream variables as a JSON data structure to the standard output or standard error* (respectively).
|
||||
|
||||
* Use **tee** which formats the current stream record (not just an arbitrary string as with **print**) to a specific file.
|
||||
|
||||
* Use **emit**/**emitp**/**emitf** to send out-of-stream variables' current values to the output record stream, e.g. ``@sum += $x; emit @sum`` which produces an extra output record such as ``sum=3.1648382``.
|
||||
* Use **emit**/**emitp**/**emitf** to send out-of-stream variables' current values to the output record stream, e.g. `@sum += $x; emit @sum` which produces an extra output record such as `sum=3.1648382`.
|
||||
|
||||
For the first two options you are populating the output-records stream which feeds into the next verb in a ``then``-chain (if any), or which otherwise is formatted for output using ``--o...`` flags.
|
||||
For the first two options you are populating the output-records stream which feeds into the next verb in a `then`-chain (if any), or which otherwise is formatted for output using `--o...` flags.
|
||||
|
||||
For the last three options you are sending output directly to standard output, standard error, or a file.
|
||||
|
||||
.. _reference-dsl-print-statements:
|
||||
|
||||
## Print statements
|
||||
|
||||
The ``print`` statement is perhaps self-explanatory, but with a few light caveats:
|
||||
The `print` statement is perhaps self-explanatory, but with a few light caveats:
|
||||
|
||||
* There are four variants: ``print`` goes to stdout with final newline, ``printn`` goes to stdout without final newline (you can include one using "\n" in your output string), ``eprint`` goes to stderr with final newline, and ``eprintn`` goes to stderr without final newline.
|
||||
* There are four variants: `print` goes to stdout with final newline, `printn` goes to stdout without final newline (you can include one using "\n" in your output string), `eprint` goes to stderr with final newline, and `eprintn` goes to stderr without final newline.
|
||||
|
||||
* Output goes directly to stdout/stderr, respectively: data produced this way do not go downstream to the next verb in a ``then``-chain. (Use ``emit`` for that.)
|
||||
* Output goes directly to stdout/stderr, respectively: data produced this way do not go downstream to the next verb in a `then`-chain. (Use `emit` for that.)
|
||||
|
||||
* Print statements are for strings (``print "hello"``), or things which can be made into strings: numbers (``print 3``, ``print $a + $b``, or concatenations thereof (``print "a + b = " . ($a + $b)``). Maps (in ``$*``, map-valued out-of-stream or local variables, and map literals) aren't convertible into strings. If you print a map, you get ``{is-a-map}`` as output. Please use ``dump`` to print maps.
|
||||
* Print statements are for strings (`print "hello"`), or things which can be made into strings: numbers (`print 3`, `print $a + $b`, or concatenations thereof (`print "a + b = " . ($a + $b)`). Maps (in `$*`, map-valued out-of-stream or local variables, and map literals) aren't convertible into strings. If you print a map, you get `{is-a-map}` as output. Please use `dump` to print maps.
|
||||
|
||||
* You can redirect print output to a file: ``mlr --from myfile.dat put 'print > "tap.txt", $x'`` ``mlr --from myfile.dat put 'o=$*; print > $a.".txt", $x'``.
|
||||
* You can redirect print output to a file: `mlr --from myfile.dat put 'print > "tap.txt", $x'` `mlr --from myfile.dat put 'o=$*; print > $a.".txt", $x'`.
|
||||
|
||||
* See also :ref:`reference-dsl-redirected-output-statements` for examples.
|
||||
|
||||
.. _reference-dsl-dump-statements:
|
||||
* See also [Redirected-output statements](reference-dsl-output-statements.md#redirected-output-statements) for examples.
|
||||
|
||||
## Dump statements
|
||||
|
||||
The ``dump`` statement is for printing expressions, including maps, directly to stdout/stderr, respectively:
|
||||
The `dump` statement is for printing expressions, including maps, directly to stdout/stderr, respectively:
|
||||
|
||||
* There are two variants: ``dump`` prints to stdout; ``edump`` prints to stderr.
|
||||
* There are two variants: `dump` prints to stdout; `edump` prints to stderr.
|
||||
|
||||
* Output goes directly to stdout/stderr, respectively: data produced this way do not go downstream to the next verb in a ``then``-chain. (Use ``emit`` for that.)
|
||||
* Output goes directly to stdout/stderr, respectively: data produced this way do not go downstream to the next verb in a `then`-chain. (Use `emit` for that.)
|
||||
|
||||
* You can use ``dump`` to output single strings, numbers, or expressions including map-valued data. Map-valued data are printed as JSON. Miller allows string and integer keys in its map literals while JSON allows only string keys, so use ``mlr put --jknquoteint`` if you want integer-valued map keys not double-quoted.
|
||||
* You can use `dump` to output single strings, numbers, or expressions including map-valued data. Map-valued data are printed as JSON. Miller allows string and integer keys in its map literals while JSON allows only string keys, so use `mlr put --jknquoteint` if you want integer-valued map keys not double-quoted.
|
||||
|
||||
* If you use ``dump`` (or ``edump``) with no arguments, you get a JSON structure representing the current values of all out-of-stream variables.
|
||||
* If you use `dump` (or `edump`) with no arguments, you get a JSON structure representing the current values of all out-of-stream variables.
|
||||
|
||||
* As with ``print``, you can redirect output to files.
|
||||
* As with `print`, you can redirect output to files.
|
||||
|
||||
* See also :ref:`reference-dsl-redirected-output-statements` for examples.
|
||||
* See also [Redirected-output statements](reference-dsl-output-statements.md#redirected-output-statements) for examples.
|
||||
|
||||
## Tee statements
|
||||
|
||||
Records produced by a ``mlr put`` go downstream to the next verb in your ``then``-chain, if any, or otherwise to standard output. If you want to additionally copy out records to files, you can do that using ``tee``.
|
||||
Records produced by a `mlr put` go downstream to the next verb in your `then`-chain, if any, or otherwise to standard output. If you want to additionally copy out records to files, you can do that using `tee`.
|
||||
|
||||
The syntax is, by example, ``mlr --from myfile.dat put 'tee > "tap.dat", $*' then sort -n index``. First is ``tee >``, then the filename expression (which can be an expression such as ``"tap.".$a.".dat"``), then a comma, then ``$*``. (Nothing else but ``$*`` is teeable.)
|
||||
The syntax is, by example, `mlr --from myfile.dat put 'tee > "tap.dat", $*' then sort -n index`. First is `tee >`, then the filename expression (which can be an expression such as `"tap.".$a.".dat"`), then a comma, then `$*`. (Nothing else but `$*` is teeable.)
|
||||
|
||||
See also :ref:`reference-dsl-redirected-output-statements` for examples.
|
||||
|
||||
.. _reference-dsl-redirected-output-statements:
|
||||
See also [Redirected-output statements](reference-dsl-output-statements.md#redirected-output-statements) for examples.
|
||||
|
||||
## Redirected-output statements
|
||||
|
||||
|
|
@ -66,7 +60,7 @@ The **print**, **dump** **tee**, **emitf**, **emit**, and **emitp** keywords all
|
|||
|
||||
Details:
|
||||
|
||||
* The ``print`` and ``dump`` keywords produce output immediately to standard output, or to specified file(s) or pipe-to command if present.
|
||||
* The `print` and `dump` keywords produce output immediately to standard output, or to specified file(s) or pipe-to command if present.
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr help keyword print
|
||||
|
|
@ -76,13 +70,13 @@ GENMD_RUN_COMMAND
|
|||
mlr help keyword dump
|
||||
GENMD_EOF
|
||||
|
||||
* ``mlr put`` sends the current record (possibly modified by the ``put`` expression) to the output record stream. Records are then input to the following verb in a ``then``-chain (if any), else printed to standard output (unless ``put -q``). The **tee** keyword *additionally* writes the output record to specified file(s) or pipe-to command, or immediately to ``stdout``/``stderr``.
|
||||
* `mlr put` sends the current record (possibly modified by the `put` expression) to the output record stream. Records are then input to the following verb in a `then`-chain (if any), else printed to standard output (unless `put -q`). The **tee** keyword *additionally* writes the output record to specified file(s) or pipe-to command, or immediately to `stdout`/`stderr`.
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr help keyword tee
|
||||
GENMD_EOF
|
||||
|
||||
* ``mlr put``'s ``emitf``, ``emitp``, and ``emit`` send out-of-stream variables to the output record stream. These are then input to the following verb in a ``then``-chain (if any), else printed to standard output. When redirected with ``>``, ``>>``, or ``|``, they *instead* write the out-of-stream variable(s) to specified file(s) or pipe-to command, or immediately to ``stdout``/``stderr``.
|
||||
* `mlr put`'s `emitf`, `emitp`, and `emit` send out-of-stream variables to the output record stream. These are then input to the following verb in a `then`-chain (if any), else printed to standard output. When redirected with `>`, `>>`, or `|`, they *instead* write the out-of-stream variable(s) to specified file(s) or pipe-to command, or immediately to `stdout`/`stderr`.
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr help keyword emitf
|
||||
|
|
@ -96,13 +90,11 @@ GENMD_RUN_COMMAND
|
|||
mlr help keyword emit
|
||||
GENMD_EOF
|
||||
|
||||
.. _reference-dsl-emit-statements:
|
||||
|
||||
## Emit statements
|
||||
|
||||
There are three variants: ``emitf``, ``emit``, and ``emitp``. Keep in mind that out-of-stream variables are a nested, multi-level hashmap (directly viewable as JSON using ``dump``), whereas Miller output records are lists of single-level key-value pairs. The three emit variants allow you to control how the multilevel hashmaps are flatten down to output records. You can emit any map-valued expression, including ``$*``, map-valued out-of-stream variables, the entire out-of-stream-variable collection ``@*``, map-valued local variables, map literals, or map-valued function return values.
|
||||
There are three variants: `emitf`, `emit`, and `emitp`. Keep in mind that out-of-stream variables are a nested, multi-level hashmap (directly viewable as JSON using `dump`), whereas Miller output records are lists of single-level key-value pairs. The three emit variants allow you to control how the multilevel hashmaps are flatten down to output records. You can emit any map-valued expression, including `$*`, map-valued out-of-stream variables, the entire out-of-stream-variable collection `@*`, map-valued local variables, map literals, or map-valued function return values.
|
||||
|
||||
Use **emitf** to output several out-of-stream variables side-by-side in the same output record. For ``emitf`` these mustn't have indexing using ``@name[...]``. Example:
|
||||
Use **emitf** to output several out-of-stream variables side-by-side in the same output record. For `emitf` these mustn't have indexing using `@name[...]`. Example:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr put -q '
|
||||
|
|
@ -127,7 +119,7 @@ GENMD_RUN_COMMAND
|
|||
mlr put -q '@sum += $x; end { emit @sum }' data/small
|
||||
GENMD_EOF
|
||||
|
||||
If it's indexed then use as many names after ``emit`` as there are indices:
|
||||
If it's indexed then use as many names after `emit` as there are indices:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr put -q '@sum[$a] += $x; end { dump }' data/small
|
||||
|
|
@ -156,7 +148,7 @@ mlr put -q '
|
|||
' data/small
|
||||
GENMD_EOF
|
||||
|
||||
Now for **emitp**: if you have as many names following ``emit`` as there are levels in the out-of-stream variable's hashmap, then ``emit`` and ``emitp`` do the same thing. Where they differ is when you don't specify as many names as there are hashmap levels. In this case, Miller needs to flatten multiple map indices down to output-record keys: ``emitp`` includes full prefixing (hence the ``p`` in ``emitp``) while ``emit`` takes the deepest hashmap key as the output-record key:
|
||||
Now for **emitp**: if you have as many names following `emit` as there are levels in the out-of-stream variable's hashmap, then `emit` and `emitp` do the same thing. Where they differ is when you don't specify as many names as there are hashmap levels. In this case, Miller needs to flatten multiple map indices down to output-record keys: `emitp` includes full prefixing (hence the `p` in `emitp`) while `emit` takes the deepest hashmap key as the output-record key:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr put -q '@sum[$a][$b] += $x; end { dump }' data/small
|
||||
|
|
@ -183,7 +175,7 @@ mlr --oxtab put -q '@sum[$a][$b] += $x; end { emitp @sum }' data/small
|
|||
GENMD_EOF
|
||||
|
||||
Use **--oflatsep** to specify the character which joins multilevel
|
||||
keys for ``emitp`` (it defaults to a colon):
|
||||
keys for `emitp` (it defaults to a colon):
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr put -q --oflatsep / '@sum[$a][$b] += $x; end { emitp @sum, "a" }' data/small
|
||||
|
|
@ -207,11 +199,11 @@ including their names in parentheses:
|
|||
|
||||
GENMD_INCLUDE_AND_RUN_ESCAPED(data/emit-lashed.sh)
|
||||
|
||||
What this does is walk through the first out-of-stream variable (``@x_sum`` in this example) as usual, then for each keylist found (e.g. ``pan,wye``), include the values for the remaining out-of-stream variables (here, ``@x_count`` and ``@x_mean``). You should use this when all out-of-stream variables in the emit statement have **the same shape and the same keylists**.
|
||||
What this does is walk through the first out-of-stream variable (`@x_sum` in this example) as usual, then for each keylist found (e.g. `pan,wye`), include the values for the remaining out-of-stream variables (here, `@x_count` and `@x_mean`). You should use this when all out-of-stream variables in the emit statement have **the same shape and the same keylists**.
|
||||
|
||||
## Emit-all statements
|
||||
|
||||
Use **emit all** (or ``emit @*`` which is synonymous) to output all out-of-stream variables. You can use the following idiom to get various accumulators output side-by-side (reminiscent of ``mlr stats1``):
|
||||
Use **emit all** (or `emit @*` which is synonymous) to output all out-of-stream variables. You can use the following idiom to get various accumulators output side-by-side (reminiscent of `mlr stats1`):
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --from data/small --opprint put -q '
|
||||
|
|
|
|||
|
|
@ -5,8 +5,10 @@
|
|||
|
||||
Multiple expressions may be given, separated by semicolons, and each may refer to the ones before:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>ruby -e '10.times{|i|puts "i=#{i}"}' | mlr --opprint put '$j = $i + 1; $k = $i +$j'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
i j k
|
||||
0 1 1
|
||||
1 2 3
|
||||
|
|
@ -22,7 +24,7 @@ i j k
|
|||
|
||||
Newlines within the expression are ignored, which can help increase legibility of complex expressions:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --opprint put '</b>
|
||||
<b> $nf = NF;</b>
|
||||
<b> $nr = NR;</b>
|
||||
|
|
@ -30,6 +32,8 @@ Newlines within the expression are ignored, which can help increase legibility o
|
|||
<b> $filenum = FILENUM;</b>
|
||||
<b> $filename = FILENAME</b>
|
||||
<b>' data/small data/small2</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a b i x y nf nr fnr filenum filename
|
||||
pan pan 1 0.3467901443380824 0.7268028627434533 5 1 1 1 data/small
|
||||
eks pan 2 0.7586799647899636 0.5221511083334797 5 2 2 1 data/small
|
||||
|
|
@ -43,22 +47,24 @@ hat wye 10002 0.321507044286237609 0.568893318795083758 5 9 4 2 data/s
|
|||
pan zee 10003 0.272054845593895200 0.425789896597056627 5 10 5 2 data/small2
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --opprint filter '($x > 0.5 && $y < 0.5) || ($x < 0.5 && $y > 0.5)' \</b>
|
||||
<b> then stats2 -a corr -f x,y \</b>
|
||||
<b> data/medium</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
x_y_corr
|
||||
-0.7479940285189345
|
||||
</pre>
|
||||
|
||||
.. _reference-dsl-expressions-from-files:
|
||||
|
||||
## Expressions from files
|
||||
|
||||
The simplest way to enter expressions for ``put`` and ``filter`` is between single quotes on the command line, e.g.
|
||||
The simplest way to enter expressions for `put` and `filter` is between single quotes on the command line, e.g.
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from data/small put '$xy = sqrt($x**2 + $y**2)'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,b=pan,i=1,x=0.3467901443380824,y=0.7268028627434533,xy=0.8052985815845617
|
||||
a=eks,b=pan,i=2,x=0.7586799647899636,y=0.5221511083334797,xy=0.9209978658539777
|
||||
a=wye,b=wye,i=3,x=0.20460330576630303,y=0.33831852551664776,xy=0.3953756915115773
|
||||
|
|
@ -66,8 +72,10 @@ a=eks,b=wye,i=4,x=0.38139939387114097,y=0.13418874328430463,xy=0.404316851577441
|
|||
a=wye,b=pan,i=5,x=0.5732889198020006,y=0.8636244699032729,xy=1.036584492737304
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from data/small put 'func f(a, b) { return sqrt(a**2 + b**2) } $xy = f($x, $y)'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,b=pan,i=1,x=0.3467901443380824,y=0.7268028627434533,xy=0.8052985815845617
|
||||
a=eks,b=pan,i=2,x=0.7586799647899636,y=0.5221511083334797,xy=0.9209978658539777
|
||||
a=wye,b=wye,i=3,x=0.20460330576630303,y=0.33831852551664776,xy=0.3953756915115773
|
||||
|
|
@ -78,16 +86,20 @@ a=wye,b=pan,i=5,x=0.5732889198020006,y=0.8636244699032729,xy=1.036584492737304
|
|||
You may, though, find it convenient to put expressions into files for reuse, and read them
|
||||
**using the -f option**. For example:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/fe-example-3.mlr</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
func f(a, b) {
|
||||
return sqrt(a**2 + b**2)
|
||||
}
|
||||
$xy = f($x, $y)
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from data/small put -f data/fe-example-3.mlr</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,b=pan,i=1,x=0.3467901443380824,y=0.7268028627434533,xy=0.8052985815845617
|
||||
a=eks,b=pan,i=2,x=0.7586799647899636,y=0.5221511083334797,xy=0.9209978658539777
|
||||
a=wye,b=wye,i=3,x=0.20460330576630303,y=0.33831852551664776,xy=0.3953756915115773
|
||||
|
|
@ -97,15 +109,19 @@ a=wye,b=pan,i=5,x=0.5732889198020006,y=0.8636244699032729,xy=1.036584492737304
|
|||
|
||||
If you have some of the logic in a file and you want to write the rest on the command line, you can **use the -f and -e options together**:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/fe-example-4.mlr</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
func f(a, b) {
|
||||
return sqrt(a**2 + b**2)
|
||||
}
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from data/small put -f data/fe-example-4.mlr -e '$xy = f($x, $y)'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,b=pan,i=1,x=0.3467901443380824,y=0.7268028627434533,xy=0.8052985815845617
|
||||
a=eks,b=pan,i=2,x=0.7586799647899636,y=0.5221511083334797,xy=0.9209978658539777
|
||||
a=wye,b=wye,i=3,x=0.20460330576630303,y=0.33831852551664776,xy=0.3953756915115773
|
||||
|
|
@ -115,15 +131,15 @@ a=wye,b=pan,i=5,x=0.5732889198020006,y=0.8636244699032729,xy=1.036584492737304
|
|||
|
||||
A suggested use-case here is defining functions in files, and calling them from command-line expressions.
|
||||
|
||||
Another suggested use-case is putting default parameter values in files, e.g. using ``begin{@count=is_present(@count)?@count:10}`` in the file, where you can precede that using ``begin{@count=40}`` using ``-e``.
|
||||
Another suggested use-case is putting default parameter values in files, e.g. using `begin{@count=is_present(@count)?@count:10}` in the file, where you can precede that using `begin{@count=40}` using `-e`.
|
||||
|
||||
Moreover, you can have one or more ``-f`` expressions (maybe one function per file, for example) and one or more ``-e`` expressions on the command line. If you mix ``-f`` and ``-e`` then the expressions are evaluated in the order encountered. (Since the expressions are all simply concatenated together in order, don't forget intervening semicolons: e.g. not ``mlr put -e '$x=1' -e '$y=2 ...'`` but rather ``mlr put -e '$x=1;' -e '$y=2' ...``.)
|
||||
Moreover, you can have one or more `-f` expressions (maybe one function per file, for example) and one or more `-e` expressions on the command line. If you mix `-f` and `-e` then the expressions are evaluated in the order encountered. (Since the expressions are all simply concatenated together in order, don't forget intervening semicolons: e.g. not `mlr put -e '$x=1' -e '$y=2 ...'` but rather `mlr put -e '$x=1;' -e '$y=2' ...`.)
|
||||
|
||||
## Semicolons, commas, newlines, and curly braces
|
||||
|
||||
Miller uses **semicolons as statement separators**, not statement terminators. This means you can write:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
mlr put 'x=1'
|
||||
mlr put 'x=1;$y=2'
|
||||
mlr put 'x=1;$y=2;'
|
||||
|
|
@ -132,19 +148,23 @@ mlr put 'x=1;;;;$y=2;'
|
|||
|
||||
Semicolons are optional after closing curly braces (which close conditionals and loops as discussed below).
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>echo x=1,y=2 | mlr put 'while (NF < 10) { $[NF+1] = ""} $foo = "bar"'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
x=1,y=2,3=,4=,5=,6=,7=,8=,9=,10=,foo=bar
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>echo x=1,y=2 | mlr put 'while (NF < 10) { $[NF+1] = ""}; $foo = "bar"'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
x=1,y=2,3=,4=,5=,6=,7=,8=,9=,10=,foo=bar
|
||||
</pre>
|
||||
|
||||
Semicolons are required between statements even if those statements are on separate lines. **Newlines** are for your convenience but have no syntactic meaning: line endings do not terminate statements. For example, adjacent assignment statements must be separated by semicolons even if those statements are on separate lines:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
mlr put '
|
||||
$x = 1
|
||||
$y = 2 # Syntax error
|
||||
|
|
@ -158,7 +178,7 @@ mlr put '
|
|||
|
||||
**Trailing commas** are allowed in function/subroutine definitions, function/subroutine callsites, and map literals. This is intended for (although not restricted to) the multi-line case:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --csvlite --from data/a.csv put '</b>
|
||||
<b> func f(</b>
|
||||
<b> num a,</b>
|
||||
|
|
@ -176,6 +196,8 @@ mlr put '
|
|||
<b> "v": NR,</b>
|
||||
<b> }</b>
|
||||
<b>'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
s,t,u,v
|
||||
3,-1,5,1
|
||||
9,-1,41,2
|
||||
|
|
@ -183,17 +205,17 @@ s,t,u,v
|
|||
|
||||
Bodies for all compound statements must be enclosed in **curly braces**, even if the body is a single statement:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put 'if ($x == 1) $y = 2' # Syntax error</b>
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put 'if ($x == 1) { $y = 2 }' # This is OK</b>
|
||||
</pre>
|
||||
|
||||
Bodies for compound statements may be empty:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put 'if ($x == 1) { }' # This no-op is syntactically acceptable</b>
|
||||
</pre>
|
||||
|
||||
|
|
|
|||
|
|
@ -18,11 +18,9 @@ mlr --opprint filter '($x > 0.5 && $y < 0.5) || ($x < 0.5 && $y > 0.5)' \
|
|||
data/medium
|
||||
GENMD_EOF
|
||||
|
||||
.. _reference-dsl-expressions-from-files:
|
||||
|
||||
## Expressions from files
|
||||
|
||||
The simplest way to enter expressions for ``put`` and ``filter`` is between single quotes on the command line, e.g.
|
||||
The simplest way to enter expressions for `put` and `filter` is between single quotes on the command line, e.g.
|
||||
|
||||
GENMD_INCLUDE_AND_RUN_ESCAPED(data/fe-example-1.sh)
|
||||
|
||||
|
|
@ -51,9 +49,9 @@ GENMD_EOF
|
|||
|
||||
A suggested use-case here is defining functions in files, and calling them from command-line expressions.
|
||||
|
||||
Another suggested use-case is putting default parameter values in files, e.g. using ``begin{@count=is_present(@count)?@count:10}`` in the file, where you can precede that using ``begin{@count=40}`` using ``-e``.
|
||||
Another suggested use-case is putting default parameter values in files, e.g. using `begin{@count=is_present(@count)?@count:10}` in the file, where you can precede that using `begin{@count=40}` using `-e`.
|
||||
|
||||
Moreover, you can have one or more ``-f`` expressions (maybe one function per file, for example) and one or more ``-e`` expressions on the command line. If you mix ``-f`` and ``-e`` then the expressions are evaluated in the order encountered. (Since the expressions are all simply concatenated together in order, don't forget intervening semicolons: e.g. not ``mlr put -e '$x=1' -e '$y=2 ...'`` but rather ``mlr put -e '$x=1;' -e '$y=2' ...``.)
|
||||
Moreover, you can have one or more `-f` expressions (maybe one function per file, for example) and one or more `-e` expressions on the command line. If you mix `-f` and `-e` then the expressions are evaluated in the order encountered. (Since the expressions are all simply concatenated together in order, don't forget intervening semicolons: e.g. not `mlr put -e '$x=1' -e '$y=2 ...'` but rather `mlr put -e '$x=1;' -e '$y=2' ...`.)
|
||||
|
||||
## Semicolons, commas, newlines, and curly braces
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
<!--- PLEASE DO NOT EDIT DIRECTLY. EDIT THE .md.in FILE PLEASE. --->
|
||||
DSL reference: unset statements
|
||||
#
|
||||
You can clear a map key by assigning the empty string as its value: ``$x=""`` or ``@x=""``. Using ``unset`` you can remove the key entirely. Examples:
|
||||
You can clear a map key by assigning the empty string as its value: `$x=""` or `@x=""`. Using `unset` you can remove the key entirely. Examples:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,b=pan,i=1,x=0.3467901443380824,y=0.7268028627434533
|
||||
a=eks,b=pan,i=2,x=0.7586799647899636,y=0.5221511083334797
|
||||
a=wye,b=wye,i=3,x=0.20460330576630303,y=0.33831852551664776
|
||||
|
|
@ -12,8 +14,10 @@ a=eks,b=wye,i=4,x=0.38139939387114097,y=0.13418874328430463
|
|||
a=wye,b=pan,i=5,x=0.5732889198020006,y=0.8636244699032729
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put 'unset $x, $a' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
b=pan,i=1,y=0.7268028627434533
|
||||
b=pan,i=2,y=0.5221511083334797
|
||||
b=wye,i=3,y=0.33831852551664776
|
||||
|
|
@ -21,10 +25,12 @@ b=wye,i=4,y=0.13418874328430463
|
|||
b=pan,i=5,y=0.8636244699032729
|
||||
</pre>
|
||||
|
||||
This can also be done, of course, using ``mlr cut -x``. You can also clear out-of-stream or local variables, at the base name level, or at an indexed sublevel:
|
||||
This can also be done, of course, using `mlr cut -x`. You can also clear out-of-stream or local variables, at the base name level, or at an indexed sublevel:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put -q '@sum[$a][$b] += $x; end { dump; unset @sum; dump }' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
{
|
||||
"sum": {
|
||||
"pan": {
|
||||
|
|
@ -43,8 +49,10 @@ This can also be done, of course, using ``mlr cut -x``. You can also clear out-o
|
|||
{}
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put -q '@sum[$a][$b] += $x; end { dump; unset @sum["eks"]; dump }' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
{
|
||||
"sum": {
|
||||
"pan": {
|
||||
|
|
@ -73,4 +81,4 @@ This can also be done, of course, using ``mlr cut -x``. You can also clear out-o
|
|||
}
|
||||
</pre>
|
||||
|
||||
If you use ``unset all`` (or ``unset @*`` which is synonymous), that will unset all out-of-stream variables which have been defined up to that point.
|
||||
If you use `unset all` (or `unset @*` which is synonymous), that will unset all out-of-stream variables which have been defined up to that point.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
DSL reference: unset statements
|
||||
#
|
||||
You can clear a map key by assigning the empty string as its value: ``$x=""`` or ``@x=""``. Using ``unset`` you can remove the key entirely. Examples:
|
||||
You can clear a map key by assigning the empty string as its value: `$x=""` or `@x=""`. Using `unset` you can remove the key entirely. Examples:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
cat data/small
|
||||
|
|
@ -10,7 +10,7 @@ GENMD_RUN_COMMAND
|
|||
mlr put 'unset $x, $a' data/small
|
||||
GENMD_EOF
|
||||
|
||||
This can also be done, of course, using ``mlr cut -x``. You can also clear out-of-stream or local variables, at the base name level, or at an indexed sublevel:
|
||||
This can also be done, of course, using `mlr cut -x`. You can also clear out-of-stream or local variables, at the base name level, or at an indexed sublevel:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr put -q '@sum[$a][$b] += $x; end { dump; unset @sum; dump }' data/small
|
||||
|
|
@ -20,4 +20,4 @@ GENMD_RUN_COMMAND
|
|||
mlr put -q '@sum[$a][$b] += $x; end { dump; unset @sum["eks"]; dump }' data/small
|
||||
GENMD_EOF
|
||||
|
||||
If you use ``unset all`` (or ``unset @*`` which is synonymous), that will unset all out-of-stream variables which have been defined up to that point.
|
||||
If you use `unset all` (or `unset @*` which is synonymous), that will unset all out-of-stream variables which have been defined up to that point.
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ As of Miller 5.0.0 you can define your own functions, as well as subroutines.
|
|||
|
||||
Here's the obligatory example of a recursive function to compute the factorial function:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --opprint --from data/small put '</b>
|
||||
<b> func f(n) {</b>
|
||||
<b> if (is_numeric(n)) {</b>
|
||||
|
|
@ -22,6 +22,8 @@ Here's the obligatory example of a recursive function to compute the factorial f
|
|||
<b> $ox = f($x + NR);</b>
|
||||
<b> $oi = f($i);</b>
|
||||
<b>'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a b i x y ox oi
|
||||
pan pan 1 0.3467901443380824 0.7268028627434533 0.46705354854811026 1
|
||||
eks pan 2 0.7586799647899636 0.5221511083334797 3.680838410072862 2
|
||||
|
|
@ -32,29 +34,29 @@ wye pan 5 0.5732889198020006 0.8636244699032729 211.38730958519247 120
|
|||
|
||||
Properties of user-defined functions:
|
||||
|
||||
* Function bodies start with ``func`` and a parameter list, defined outside of ``begin``, ``end``, or other ``func`` or ``subr`` blocks. (I.e. the Miller DSL has no nested functions.)
|
||||
* Function bodies start with `func` and a parameter list, defined outside of `begin`, `end`, or other `func` or `subr` blocks. (I.e. the Miller DSL has no nested functions.)
|
||||
|
||||
* A function (uniqified by its name) may not be redefined: either by redefining a user-defined function, or by redefining a built-in function. However, functions and subroutines have separate namespaces: you can define a subroutine ``log`` which does not clash with the mathematical ``log`` function.
|
||||
* A function (uniqified by its name) may not be redefined: either by redefining a user-defined function, or by redefining a built-in function. However, functions and subroutines have separate namespaces: you can define a subroutine `log` which does not clash with the mathematical `log` function.
|
||||
|
||||
* Functions may be defined either before or after use (there is an object-binding/linkage step at startup). More specifically, functions may be either recursive or mutually recursive. Functions may not call subroutines.
|
||||
|
||||
* Functions may be defined and called either within ``mlr put`` or ``mlr put``.
|
||||
* Functions may be defined and called either within `mlr put` or `mlr put`.
|
||||
|
||||
* Functions have read access to ``$``-variables and ``@``-variables but may not modify them. See also :ref:`cookbook-memoization-with-oosvars` for an example.
|
||||
* Functions have read access to `$`-variables and `@`-variables but may not modify them. See also [Memoization with out-of-stream variables](misc-examples.md#memoization-with-out-of-stream-variables) for an example.
|
||||
|
||||
* Argument values may be reassigned: they are not read-only.
|
||||
|
||||
* When a return value is not implicitly returned, this results in a return value of absent-null. (In the example above, if there were records for which the argument to ``f`` is non-numeric, the assignments would be skipped.) See also the section on [xxxx](reference-main-null-data.md).
|
||||
* When a return value is not implicitly returned, this results in a return value of absent-null. (In the example above, if there were records for which the argument to `f` is non-numeric, the assignments would be skipped.) See also the section on [xxxx](reference-main-null-data.md).
|
||||
|
||||
* See the section on :ref:`reference-dsl-local-variables` for information on scope and extent of arguments, as well as for information on the use of local variables within functions.
|
||||
* See the section on [Local variables](reference-dsl-variables.md#local-variables) for information on scope and extent of arguments, as well as for information on the use of local variables within functions.
|
||||
|
||||
* See the section on :ref:`reference-dsl-expressions-from-files` for information on the use of ``-f`` and ``-e`` flags.
|
||||
* See the section on [Expressions from files](reference-dsl-syntax.md#expressions-from-files) for information on the use of `-f` and `-e` flags.
|
||||
|
||||
## User-defined subroutines
|
||||
|
||||
Example:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --opprint --from data/small put -q '</b>
|
||||
<b> begin {</b>
|
||||
<b> @call_count = 0;</b>
|
||||
|
|
@ -72,6 +74,8 @@ Example:
|
|||
<b> print "NR=" . NR;</b>
|
||||
<b> call s(NR);</b>
|
||||
<b>'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
NR=1
|
||||
numcalls=1
|
||||
NR=2
|
||||
|
|
@ -86,18 +90,18 @@ numcalls=15
|
|||
|
||||
Properties of user-defined subroutines:
|
||||
|
||||
* Subroutine bodies start with ``subr`` and a parameter list, defined outside of ``begin``, ``end``, or other ``func`` or ``subr`` blocks. (I.e. the Miller DSL has no nested subroutines.)
|
||||
* Subroutine bodies start with `subr` and a parameter list, defined outside of `begin`, `end`, or other `func` or `subr` blocks. (I.e. the Miller DSL has no nested subroutines.)
|
||||
|
||||
* A subroutine (uniqified by its name) may not be redefined. However, functions and subroutines have separate namespaces: you can define a subroutine ``log`` which does not clash with the mathematical ``log`` function.
|
||||
* A subroutine (uniqified by its name) may not be redefined. However, functions and subroutines have separate namespaces: you can define a subroutine `log` which does not clash with the mathematical `log` function.
|
||||
|
||||
* Subroutines may be defined either before or after use (there is an object-binding/linkage step at startup). More specifically, subroutines may be either recursive or mutually recursive. Subroutines may call functions.
|
||||
|
||||
* Subroutines may be defined and called either within ``mlr put`` or ``mlr put``.
|
||||
* Subroutines may be defined and called either within `mlr put` or `mlr put`.
|
||||
|
||||
* Subroutines have read/write access to ``$``-variables and ``@``-variables.
|
||||
* Subroutines have read/write access to `$`-variables and `@`-variables.
|
||||
|
||||
* Argument values may be reassigned: they are not read-only.
|
||||
|
||||
* See the section on :ref:`reference-dsl-local-variables` for information on scope and extent of arguments, as well as for information on the use of local variables within functions.
|
||||
* See the section on [local variables](reference-dsl-variables.md#local-variables) for information on scope and extent of arguments, as well as for information on the use of local variables within functions.
|
||||
|
||||
* See the section on :ref:`reference-dsl-expressions-from-files` for information on the use of ``-f`` and ``-e`` flags.
|
||||
* See the section on [Expressions from files](reference-dsl-syntax.md#expressions-from-files) for information on the use of `-f` and `-e` flags.
|
||||
|
|
|
|||
|
|
@ -10,23 +10,23 @@ GENMD_INCLUDE_AND_RUN_ESCAPED(data/factorial-example.sh)
|
|||
|
||||
Properties of user-defined functions:
|
||||
|
||||
* Function bodies start with ``func`` and a parameter list, defined outside of ``begin``, ``end``, or other ``func`` or ``subr`` blocks. (I.e. the Miller DSL has no nested functions.)
|
||||
* Function bodies start with `func` and a parameter list, defined outside of `begin`, `end`, or other `func` or `subr` blocks. (I.e. the Miller DSL has no nested functions.)
|
||||
|
||||
* A function (uniqified by its name) may not be redefined: either by redefining a user-defined function, or by redefining a built-in function. However, functions and subroutines have separate namespaces: you can define a subroutine ``log`` which does not clash with the mathematical ``log`` function.
|
||||
* A function (uniqified by its name) may not be redefined: either by redefining a user-defined function, or by redefining a built-in function. However, functions and subroutines have separate namespaces: you can define a subroutine `log` which does not clash with the mathematical `log` function.
|
||||
|
||||
* Functions may be defined either before or after use (there is an object-binding/linkage step at startup). More specifically, functions may be either recursive or mutually recursive. Functions may not call subroutines.
|
||||
|
||||
* Functions may be defined and called either within ``mlr put`` or ``mlr put``.
|
||||
* Functions may be defined and called either within `mlr put` or `mlr put`.
|
||||
|
||||
* Functions have read access to ``$``-variables and ``@``-variables but may not modify them. See also :ref:`cookbook-memoization-with-oosvars` for an example.
|
||||
* Functions have read access to `$`-variables and `@`-variables but may not modify them. See also [Memoization with out-of-stream variables](misc-examples.md#memoization-with-out-of-stream-variables) for an example.
|
||||
|
||||
* Argument values may be reassigned: they are not read-only.
|
||||
|
||||
* When a return value is not implicitly returned, this results in a return value of absent-null. (In the example above, if there were records for which the argument to ``f`` is non-numeric, the assignments would be skipped.) See also the section on [xxxx](reference-main-null-data.md).
|
||||
* When a return value is not implicitly returned, this results in a return value of absent-null. (In the example above, if there were records for which the argument to `f` is non-numeric, the assignments would be skipped.) See also the section on [xxxx](reference-main-null-data.md).
|
||||
|
||||
* See the section on :ref:`reference-dsl-local-variables` for information on scope and extent of arguments, as well as for information on the use of local variables within functions.
|
||||
* See the section on [Local variables](reference-dsl-variables.md#local-variables) for information on scope and extent of arguments, as well as for information on the use of local variables within functions.
|
||||
|
||||
* See the section on :ref:`reference-dsl-expressions-from-files` for information on the use of ``-f`` and ``-e`` flags.
|
||||
* See the section on [Expressions from files](reference-dsl-syntax.md#expressions-from-files) for information on the use of `-f` and `-e` flags.
|
||||
|
||||
## User-defined subroutines
|
||||
|
||||
|
|
@ -36,18 +36,18 @@ GENMD_INCLUDE_AND_RUN_ESCAPED(data/subr-example.sh)
|
|||
|
||||
Properties of user-defined subroutines:
|
||||
|
||||
* Subroutine bodies start with ``subr`` and a parameter list, defined outside of ``begin``, ``end``, or other ``func`` or ``subr`` blocks. (I.e. the Miller DSL has no nested subroutines.)
|
||||
* Subroutine bodies start with `subr` and a parameter list, defined outside of `begin`, `end`, or other `func` or `subr` blocks. (I.e. the Miller DSL has no nested subroutines.)
|
||||
|
||||
* A subroutine (uniqified by its name) may not be redefined. However, functions and subroutines have separate namespaces: you can define a subroutine ``log`` which does not clash with the mathematical ``log`` function.
|
||||
* A subroutine (uniqified by its name) may not be redefined. However, functions and subroutines have separate namespaces: you can define a subroutine `log` which does not clash with the mathematical `log` function.
|
||||
|
||||
* Subroutines may be defined either before or after use (there is an object-binding/linkage step at startup). More specifically, subroutines may be either recursive or mutually recursive. Subroutines may call functions.
|
||||
|
||||
* Subroutines may be defined and called either within ``mlr put`` or ``mlr put``.
|
||||
* Subroutines may be defined and called either within `mlr put` or `mlr put`.
|
||||
|
||||
* Subroutines have read/write access to ``$``-variables and ``@``-variables.
|
||||
* Subroutines have read/write access to `$`-variables and `@`-variables.
|
||||
|
||||
* Argument values may be reassigned: they are not read-only.
|
||||
|
||||
* See the section on :ref:`reference-dsl-local-variables` for information on scope and extent of arguments, as well as for information on the use of local variables within functions.
|
||||
* See the section on [local variables](reference-dsl-variables.md#local-variables) for information on scope and extent of arguments, as well as for information on the use of local variables within functions.
|
||||
|
||||
* See the section on :ref:`reference-dsl-expressions-from-files` for information on the use of ``-f`` and ``-e`` flags.
|
||||
* See the section on [Expressions from files](reference-dsl-syntax.md#expressions-from-files) for information on the use of `-f` and `-e` flags.
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@
|
|||
|
||||
Miller has the following kinds of variables:
|
||||
|
||||
**Built-in variables** such as ``NF``, ``NF``, ``FILENAME``, ``M_PI``, and ``M_E``. These are all capital letters and are read-only (although some of them change value from one record to another).
|
||||
**Built-in variables** such as `NF`, `NF`, `FILENAME`, `M_PI`, and `M_E`. These are all capital letters and are read-only (although some of them change value from one record to another).
|
||||
|
||||
**Fields of stream records**, accessed using the ``$`` prefix. These refer to fields of the current data-stream record. For example, in ``echo x=1,y=2 | mlr put '$z = $x + $y'``, ``$x`` and ``$y`` refer to input fields, and ``$z`` refers to a new, computed output field. In a few contexts, presented below, you can refer to the entire record as ``$*``.
|
||||
**Fields of stream records**, accessed using the `$` prefix. These refer to fields of the current data-stream record. For example, in `echo x=1,y=2 | mlr put '$z = $x + $y'`, `$x` and `$y` refer to input fields, and `$z` refers to a new, computed output field. In a few contexts, presented below, you can refer to the entire record as `$*`.
|
||||
|
||||
**Out-of-stream variables** accessed using the ``@`` prefix. These refer to data which persist from one record to the next, including in ``begin`` and ``end`` blocks (which execute before/after the record stream is consumed, respectively). You use them to remember values across records, such as sums, differences, counters, and so on. In a few contexts, presented below, you can refer to the entire out-of-stream-variables collection as ``@*``.
|
||||
**Out-of-stream variables** accessed using the `@` prefix. These refer to data which persist from one record to the next, including in `begin` and `end` blocks (which execute before/after the record stream is consumed, respectively). You use them to remember values across records, such as sums, differences, counters, and so on. In a few contexts, presented below, you can refer to the entire out-of-stream-variables collection as `@*`.
|
||||
|
||||
**Local variables** are limited in scope and extent to the current statements being executed: these include function arguments, bound variables in for loops, and explicitly declared local variables.
|
||||
|
||||
|
|
@ -15,19 +15,23 @@ Miller has the following kinds of variables:
|
|||
|
||||
## Built-in variables
|
||||
|
||||
These are written all in capital letters, such as ``NR``, ``NF``, ``FILENAME``, and only a small, specific set of them is defined by Miller.
|
||||
These are written all in capital letters, such as `NR`, `NF`, `FILENAME`, and only a small, specific set of them is defined by Miller.
|
||||
|
||||
Namely, Miller supports the following five built-in variables for :doc:`filter and put <reference-dsl>`, all ``awk``-inspired: ``NF``, ``NR``, ``FNR``, ``FILENUM``, and ``FILENAME``, as well as the mathematical constants ``M_PI`` and ``M_E``. Lastly, the ``ENV`` hashmap allows read access to environment variables, e.g. ``ENV["HOME"]`` or ``ENV["foo_".$hostname]``.
|
||||
Namely, Miller supports the following five built-in variables for [filter and put](reference-dsl.md), all `awk`-inspired: `NF`, `NR`, `FNR`, `FILENUM`, and `FILENAME`, as well as the mathematical constants `M_PI` and `M_E`. Lastly, the `ENV` hashmap allows read access to environment variables, e.g. `ENV["HOME"]` or `ENV["foo_".$hostname]`.
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr filter 'FNR == 2' data/small*</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=eks,b=pan,i=2,x=0.7586799647899636,y=0.5221511083334797
|
||||
1=pan,2=pan,3=1,4=0.3467901443380824,5=0.7268028627434533
|
||||
a=wye,b=eks,i=10000,x=0.734806020620654365,y=0.884788571337605134
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put '$fnr = FNR' data/small*</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,b=pan,i=1,x=0.3467901443380824,y=0.7268028627434533,fnr=1
|
||||
a=eks,b=pan,i=2,x=0.7586799647899636,y=0.5221511083334797,fnr=2
|
||||
a=wye,b=wye,i=3,x=0.20460330576630303,y=0.33831852551664776,fnr=3
|
||||
|
|
@ -46,19 +50,23 @@ a=hat,b=wye,i=10002,x=0.321507044286237609,y=0.568893318795083758,fnr=4
|
|||
a=pan,b=zee,i=10003,x=0.272054845593895200,y=0.425789896597056627,fnr=5
|
||||
</pre>
|
||||
|
||||
Their values of ``NF``, ``NR``, ``FNR``, ``FILENUM``, and ``FILENAME`` change from one record to the next as Miller scans through your input data stream. The mathematical constants, of course, do not change; ``ENV`` is populated from the system environment variables at the time Miller starts and is read-only for the remainder of program execution.
|
||||
Their values of `NF`, `NR`, `FNR`, `FILENUM`, and `FILENAME` change from one record to the next as Miller scans through your input data stream. The mathematical constants, of course, do not change; `ENV` is populated from the system environment variables at the time Miller starts and is read-only for the remainder of program execution.
|
||||
|
||||
Their **scope is global**: you can refer to them in any ``filter`` or ``put`` statement. Their values are assigned by the input-record reader:
|
||||
Their **scope is global**: you can refer to them in any `filter` or `put` statement. Their values are assigned by the input-record reader:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --csv put '$nr = NR' data/a.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a,b,c,nr
|
||||
1,2,3,1
|
||||
4,5,6,2
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --csv repeat -n 3 then put '$nr = NR' data/a.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a,b,c,nr
|
||||
1,2,3,1
|
||||
1,2,3,1
|
||||
|
|
@ -68,24 +76,26 @@ a,b,c,nr
|
|||
4,5,6,2
|
||||
</pre>
|
||||
|
||||
The **extent** is for the duration of the put/filter: in a ``begin`` statement (which executes before the fimd.input record is consumed) you will find ``NR=1`` and in an ``end`` statement (which is executed after the last input record is consumed) you will find ``NR`` to be the total number of records ingested.
|
||||
The **extent** is for the duration of the put/filter: in a `begin` statement (which executes before the fimd.input record is consumed) you will find `NR=1` and in an `end` statement (which is executed after the last input record is consumed) you will find `NR` to be the total number of records ingested.
|
||||
|
||||
These are all **read-only** for the ``mlr put`` and ``mlr filter`` DSLs: they may be assigned from, e.g. ``$nr=NR``, but they may not be assigned to: ``NR=100`` is a syntax error.
|
||||
These are all **read-only** for the `mlr put` and `mlr filter` DSLs: they may be assigned from, e.g. `$nr=NR`, but they may not be assigned to: `NR=100` is a syntax error.
|
||||
|
||||
## Field names
|
||||
|
||||
Names of fields within stream records must be specified using a ``$`` in :doc:`filter and put expressions <reference-dsl>`, even though the dollar signs don't appear in the data stream itself. For integer-indexed data, this looks like ``awk``'s ``$1,$2,$3``, except that Miller allows non-numeric names such as ``$quantity`` or ``$hostname``. Likewise, enclose string literals in double quotes in ``filter`` expressions even though they don't appear in file data. In particular, ``mlr filter '$x=="abc"'`` passes through the record ``x=abc``.
|
||||
Names of fields within stream records must be specified using a `$` in [filter and put expressions](reference-dsl.md), even though the dollar signs don't appear in the data stream itself. For integer-indexed data, this looks like `awk`'s `$1,$2,$3`, except that Miller allows non-numeric names such as `$quantity` or `$hostname`. Likewise, enclose string literals in double quotes in `filter` expressions even though they don't appear in file data. In particular, `mlr filter '$x=="abc"'` passes through the record `x=abc`.
|
||||
|
||||
If field names have **special characters** such as ``.`` then you can use braces, e.g. ``'${field.name}'``.
|
||||
If field names have **special characters** such as `.` then you can use braces, e.g. `'${field.name}'`.
|
||||
|
||||
You may also use a **computed field name** in square brackets, e.g.
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>echo a=3,b=4 | mlr filter '$["x"] < 0.5'</b>
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>echo s=green,t=blue,a=3,b=4 | mlr put '$[$s."_".$t] = $a * $b'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
s=green,t=blue,a=3,b=4,green_blue=12
|
||||
</pre>
|
||||
|
||||
|
|
@ -93,22 +103,24 @@ Notes:
|
|||
|
||||
The names of record fields depend on the contents of your input data stream, and their values change from one record to the next as Miller scans through your input data stream.
|
||||
|
||||
Their **extent** is limited to the current record; their **scope** is the ``filter`` or ``put`` command in which they appear.
|
||||
Their **extent** is limited to the current record; their **scope** is the `filter` or `put` command in which they appear.
|
||||
|
||||
These are **read-write**: you can do ``$y=2*$x``, ``$x=$x+1``, etc.
|
||||
These are **read-write**: you can do `$y=2*$x`, `$x=$x+1`, etc.
|
||||
|
||||
Records are Miller's output: field names present in the input stream are passed through to output (written to standard output) unless fields are removed with ``cut``, or records are excluded with ``filter`` or ``put -q``, etc. Simply assign a value to a field and it will be output.
|
||||
Records are Miller's output: field names present in the input stream are passed through to output (written to standard output) unless fields are removed with `cut`, or records are excluded with `filter` or `put -q`, etc. Simply assign a value to a field and it will be output.
|
||||
|
||||
## Positional field names
|
||||
|
||||
Even though Miller's main selling point is name-indexing, sometimes you really want to refer to a field name by its positional index (starting from 1).
|
||||
|
||||
Use ``$[[3]]`` to access the name of field 3. More generally, any expression evaluating to an integer can go between ``$[[`` and ``]]``.
|
||||
Use `$[[3]]` to access the name of field 3. More generally, any expression evaluating to an integer can go between `$[[` and `]]`.
|
||||
|
||||
Then using a computed field name, ``$[ $[[3]] ]`` is the value in the third field. This has the shorter equivalent notation ``$[[[3]]]``.
|
||||
Then using a computed field name, `$[ $[[3]] ]` is the value in the third field. This has the shorter equivalent notation `$[[[3]]]`.
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr cat data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,b=pan,i=1,x=0.3467901443380824,y=0.7268028627434533
|
||||
a=eks,b=pan,i=2,x=0.7586799647899636,y=0.5221511083334797
|
||||
a=wye,b=wye,i=3,x=0.20460330576630303,y=0.33831852551664776
|
||||
|
|
@ -116,8 +128,10 @@ a=eks,b=wye,i=4,x=0.38139939387114097,y=0.13418874328430463
|
|||
a=wye,b=pan,i=5,x=0.5732889198020006,y=0.8636244699032729
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put '$[[3]] = "NEW"' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,b=pan,NEW=1,x=0.3467901443380824,y=0.7268028627434533
|
||||
a=eks,b=pan,NEW=2,x=0.7586799647899636,y=0.5221511083334797
|
||||
a=wye,b=wye,NEW=3,x=0.20460330576630303,y=0.33831852551664776
|
||||
|
|
@ -125,8 +139,10 @@ a=eks,b=wye,NEW=4,x=0.38139939387114097,y=0.13418874328430463
|
|||
a=wye,b=pan,NEW=5,x=0.5732889198020006,y=0.8636244699032729
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put '$[[[3]]] = "NEW"' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,b=pan,i=NEW,x=0.3467901443380824,y=0.7268028627434533
|
||||
a=eks,b=pan,i=NEW,x=0.7586799647899636,y=0.5221511083334797
|
||||
a=wye,b=wye,i=NEW,x=0.20460330576630303,y=0.33831852551664776
|
||||
|
|
@ -134,8 +150,10 @@ a=eks,b=wye,i=NEW,x=0.38139939387114097,y=0.13418874328430463
|
|||
a=wye,b=pan,i=NEW,x=0.5732889198020006,y=0.8636244699032729
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put '$NEW = $[[NR]]' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,b=pan,i=1,x=0.3467901443380824,y=0.7268028627434533,NEW=a
|
||||
a=eks,b=pan,i=2,x=0.7586799647899636,y=0.5221511083334797,NEW=b
|
||||
a=wye,b=wye,i=3,x=0.20460330576630303,y=0.33831852551664776,NEW=i
|
||||
|
|
@ -143,8 +161,10 @@ a=eks,b=wye,i=4,x=0.38139939387114097,y=0.13418874328430463,NEW=x
|
|||
a=wye,b=pan,i=5,x=0.5732889198020006,y=0.8636244699032729,NEW=y
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put '$NEW = $[[[NR]]]' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,b=pan,i=1,x=0.3467901443380824,y=0.7268028627434533,NEW=pan
|
||||
a=eks,b=pan,i=2,x=0.7586799647899636,y=0.5221511083334797,NEW=pan
|
||||
a=wye,b=wye,i=3,x=0.20460330576630303,y=0.33831852551664776,NEW=3
|
||||
|
|
@ -152,8 +172,10 @@ a=eks,b=wye,i=4,x=0.38139939387114097,y=0.13418874328430463,NEW=0.38139939387114
|
|||
a=wye,b=pan,i=5,x=0.5732889198020006,y=0.8636244699032729,NEW=0.8636244699032729
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put '$[[[NR]]] = "NEW"' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=NEW,b=pan,i=1,x=0.3467901443380824,y=0.7268028627434533
|
||||
a=eks,b=NEW,i=2,x=0.7586799647899636,y=0.5221511083334797
|
||||
a=wye,b=wye,i=NEW,x=0.20460330576630303,y=0.33831852551664776
|
||||
|
|
@ -161,10 +183,12 @@ a=eks,b=wye,i=4,x=NEW,y=0.13418874328430463
|
|||
a=wye,b=pan,i=5,x=0.5732889198020006,y=NEW
|
||||
</pre>
|
||||
|
||||
Right-hand side accesses to non-existent fields -- i.e. with index less than 1 or greater than ``NF`` -- return an absent value. Likewise, left-hand side accesses only refer to fields which already exist. For example, if a field has 5 records then assigning the name or value of the 6th (or 600th) field results in a no-op.
|
||||
Right-hand side accesses to non-existent fields -- i.e. with index less than 1 or greater than `NF` -- return an absent value. Likewise, left-hand side accesses only refer to fields which already exist. For example, if a field has 5 records then assigning the name or value of the 6th (or 600th) field results in a no-op.
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put '$[[6]] = "NEW"' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,b=pan,i=1,x=0.3467901443380824,y=0.7268028627434533
|
||||
a=eks,b=pan,i=2,x=0.7586799647899636,y=0.5221511083334797
|
||||
a=wye,b=wye,i=3,x=0.20460330576630303,y=0.33831852551664776
|
||||
|
|
@ -172,8 +196,10 @@ a=eks,b=wye,i=4,x=0.38139939387114097,y=0.13418874328430463
|
|||
a=wye,b=pan,i=5,x=0.5732889198020006,y=0.8636244699032729
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put '$[[[6]]] = "NEW"' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,b=pan,i=1,x=0.3467901443380824,y=0.7268028627434533
|
||||
a=eks,b=pan,i=2,x=0.7586799647899636,y=0.5221511083334797
|
||||
a=wye,b=wye,i=3,x=0.20460330576630303,y=0.33831852551664776
|
||||
|
|
@ -183,46 +209,52 @@ a=wye,b=pan,i=5,x=0.5732889198020006,y=0.8636244699032729
|
|||
|
||||
## Out-of-stream variables
|
||||
|
||||
These are prefixed with an at-sign, e.g. ``@sum``. Furthermore, unlike built-in variables and stream-record fields, they are maintained in an arbitrarily nested hashmap: you can do ``@sum += $quanity``, or ``@sum[$color] += $quanity``, or ``@sum[$color][$shape] += $quanity``. The keys for the multi-level hashmap can be any expression which evaluates to string or integer: e.g. ``@sum[NR] = $a + $b``, ``@sum[$a."-".$b] = $x``, etc.
|
||||
These are prefixed with an at-sign, e.g. `@sum`. Furthermore, unlike built-in variables and stream-record fields, they are maintained in an arbitrarily nested hashmap: you can do `@sum += $quanity`, or `@sum[$color] += $quanity`, or `@sum[$color][$shape] += $quanity`. The keys for the multi-level hashmap can be any expression which evaluates to string or integer: e.g. `@sum[NR] = $a + $b`, `@sum[$a."-".$b] = $x`, etc.
|
||||
|
||||
Their names and their values are entirely under your control; they change only when you assign to them.
|
||||
|
||||
Just as for field names in stream records, if you want to define out-of-stream variables with **special characters** such as ``.`` then you can use braces, e.g. ``'@{variable.name}["index"]'``.
|
||||
Just as for field names in stream records, if you want to define out-of-stream variables with **special characters** such as `.` then you can use braces, e.g. `'@{variable.name}["index"]'`.
|
||||
|
||||
You may use a **computed key** in square brackets, e.g.
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>echo s=green,t=blue,a=3,b=4 | mlr put -q '@[$s."_".$t] = $a * $b; emit all'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
green_blue=12
|
||||
</pre>
|
||||
|
||||
Out-of-stream variables are **scoped** to the ``put`` command in which they appear. In particular, if you have two or more ``put`` commands separated by ``then``, each put will have its own set of out-of-stream variables:
|
||||
Out-of-stream variables are **scoped** to the `put` command in which they appear. In particular, if you have two or more `put` commands separated by `then`, each put will have its own set of out-of-stream variables:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/a.dkvp</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=1,b=2,c=3
|
||||
a=4,b=5,c=6
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put '@sum += $a; end {emit @sum}' \</b>
|
||||
<b> then put 'is_present($a) {$a=10*$a; @sum += $a}; end {emit @sum}' \</b>
|
||||
<b> data/a.dkvp</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=10,b=2,c=3
|
||||
a=40,b=5,c=6
|
||||
sum=5
|
||||
sum=50
|
||||
</pre>
|
||||
|
||||
Out-of-stream variables' **extent** is from the start to the end of the record stream, i.e. every time the ``put`` or ``filter`` statement referring to them is executed.
|
||||
Out-of-stream variables' **extent** is from the start to the end of the record stream, i.e. every time the `put` or `filter` statement referring to them is executed.
|
||||
|
||||
Out-of-stream variables are **read-write**: you can do ``$sum=@sum``, ``@sum=$sum``, etc.
|
||||
Out-of-stream variables are **read-write**: you can do `$sum=@sum`, `@sum=$sum`, etc.
|
||||
|
||||
## Indexed out-of-stream variables
|
||||
|
||||
Using an index on the ``@count`` and ``@sum`` variables, we get the benefit of the ``-g`` (group-by) option which ``mlr stats1`` and various other Miller commands have:
|
||||
Using an index on the `@count` and `@sum` variables, we get the benefit of the `-g` (group-by) option which `mlr stats1` and various other Miller commands have:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put -q '</b>
|
||||
<b> @x_count[$a] += 1;</b>
|
||||
<b> @x_sum[$a] += $x;</b>
|
||||
|
|
@ -231,6 +263,8 @@ Using an index on the ``@count`` and ``@sum`` variables, we get the benefit of t
|
|||
<b> emit @x_sum, "a";</b>
|
||||
<b> }</b>
|
||||
<b>' ./data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,x_count=1
|
||||
a=eks,x_count=2
|
||||
a=wye,x_count=2
|
||||
|
|
@ -239,8 +273,10 @@ a=eks,x_sum=1.1400793586611044
|
|||
a=wye,x_sum=0.7778922255683036
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr stats1 -a count,sum -f x -g a ./data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,x_count=1,x_sum=0.3467901443380824
|
||||
a=eks,x_count=2,x_sum=1.1400793586611044
|
||||
a=wye,x_count=2,x_sum=0.7778922255683036
|
||||
|
|
@ -248,7 +284,7 @@ a=wye,x_count=2,x_sum=0.7778922255683036
|
|||
|
||||
Indices can be arbitrarily deep -- here there are two or more of them:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from data/medium put -q '</b>
|
||||
<b> @x_count[$a][$b] += 1;</b>
|
||||
<b> @x_sum[$a][$b] += $x;</b>
|
||||
|
|
@ -256,6 +292,8 @@ Indices can be arbitrarily deep -- here there are two or more of them:
|
|||
<b> emit (@x_count, @x_sum), "a", "b";</b>
|
||||
<b> }</b>
|
||||
<b>'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,b=pan,x_count=427,x_sum=219.1851288316854
|
||||
a=pan,b=wye,x_count=395,x_sum=198.43293070748447
|
||||
a=pan,b=eks,x_count=429,x_sum=216.07522773165525
|
||||
|
|
@ -283,11 +321,11 @@ a=hat,b=hat,x_count=381,x_sum=182.8535323148762
|
|||
a=hat,b=pan,x_count=363,x_sum=168.5538067327806
|
||||
</pre>
|
||||
|
||||
The idea is that ``stats1``, and other Miller verbs, encapsulate frequently-used patterns with a minimum of keystroking (and run a little faster), whereas using out-of-stream variables you have more flexibility and control in what you do.
|
||||
The idea is that `stats1`, and other Miller verbs, encapsulate frequently-used patterns with a minimum of keystroking (and run a little faster), whereas using out-of-stream variables you have more flexibility and control in what you do.
|
||||
|
||||
Begin/end blocks can be mixed with pattern/action blocks. For example:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put '</b>
|
||||
<b> begin {</b>
|
||||
<b> @num_total = 0;</b>
|
||||
|
|
@ -302,6 +340,8 @@ Begin/end blocks can be mixed with pattern/action blocks. For example:
|
|||
<b> emitf @num_total, @num_positive</b>
|
||||
<b> }</b>
|
||||
<b>' data/put-gating-example-1.dkvp</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
x=-1
|
||||
x=0
|
||||
x=1,y=0,z=0
|
||||
|
|
@ -310,15 +350,13 @@ x=3,y=0.4771212547196624,z=0.6907396432228734
|
|||
num_total=5,num_positive=3
|
||||
</pre>
|
||||
|
||||
.. _reference-dsl-local-variables:
|
||||
|
||||
## Local variables
|
||||
|
||||
Local variables are similar to out-of-stream variables, except that their extent is limited to the expressions in which they appear (and their basenames can't be computed using square brackets). There are three kinds of local variables: **arguments** to functions/subroutines, **variables bound within for-loops**, and **locals** defined within control blocks. They may be untyped using ``var``, or typed using ``num``, ``int``, ``float``, ``str``, ``bool``, and ``map``.
|
||||
Local variables are similar to out-of-stream variables, except that their extent is limited to the expressions in which they appear (and their basenames can't be computed using square brackets). There are three kinds of local variables: **arguments** to functions/subroutines, **variables bound within for-loops**, and **locals** defined within control blocks. They may be untyped using `var`, or typed using `num`, `int`, `float`, `str`, `bool`, and `map`.
|
||||
|
||||
For example:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b># Here I'm using a specified random-number seed so this example always</b>
|
||||
<b># produces the same output for this web document: in everyday practice we</b>
|
||||
<b># would leave off the --seed 12345 part.</b>
|
||||
|
|
@ -335,6 +373,8 @@ For example:
|
|||
<b> num o = f(10, 20); # local to the top-level scope</b>
|
||||
<b> $o = o;</b>
|
||||
<b>'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
i=1,o=15.952526011537227
|
||||
i=2,o=12.782237754999116
|
||||
i=3,o=15.126606630220966
|
||||
|
|
@ -349,32 +389,34 @@ i=10,o=15.37686787628025
|
|||
|
||||
Things which are completely unsurprising, resembling many other languages:
|
||||
|
||||
* Parameter names are bound to their arguments but can be reassigned, e.g. if there is a parameter named ``a`` then you can reassign the value of ``a`` to be something else within the function if you like.
|
||||
* Parameter names are bound to their arguments but can be reassigned, e.g. if there is a parameter named `a` then you can reassign the value of `a` to be something else within the function if you like.
|
||||
|
||||
* However, you cannot redeclare the *type* of an argument or a local: ``var a=1; var a=2`` is an error but ``var a=1; a=2`` is OK.
|
||||
* However, you cannot redeclare the *type* of an argument or a local: `var a=1; var a=2` is an error but `var a=1; a=2` is OK.
|
||||
|
||||
* All argument-passing is positional rather than by name; arguments are passed by value, not by reference. (This is also true for map-valued variables: they are not, and cannot be, passed by reference)
|
||||
|
||||
* You can define locals (using ``var``, ``num``, etc.) at any scope (if-statements, else-statements, while-loops, for-loops, or the top-level scope), and nested scopes will have access (more details on scope in the next section). If you define a local variable with the same name inside an inner scope, then a new variable is created with the narrower scope.
|
||||
* You can define locals (using `var`, `num`, etc.) at any scope (if-statements, else-statements, while-loops, for-loops, or the top-level scope), and nested scopes will have access (more details on scope in the next section). If you define a local variable with the same name inside an inner scope, then a new variable is created with the narrower scope.
|
||||
|
||||
* If you assign to a local variable for the first time in a scope without declaring it as ``var``, ``num``, etc. then: if it exists in an outer scope, that outer-scope variable will be updated; if not, it will be defined in the current scope as if ``var`` had been used. (See also :ref:`reference-dsl-type-checking` for an example.) I recommend always declaring variables explicitly to make the intended scoping clear.
|
||||
* If you assign to a local variable for the first time in a scope without declaring it as `var`, `num`, etc. then: if it exists in an outer scope, that outer-scope variable will be updated; if not, it will be defined in the current scope as if `var` had been used. (See also [Type-checking](reference-dsl-variables.md#type-checking) for an example.) I recommend always declaring variables explicitly to make the intended scoping clear.
|
||||
|
||||
* Functions and subroutines never have access to locals from their callee (unless passed by value as arguments).
|
||||
|
||||
Things which are perhaps surprising compared to other languages:
|
||||
|
||||
* Type declarations using ``var``, or typed using ``num``, ``int``, ``float``, ``str``, and ``bool`` are necessary to declare local variables. Function arguments and variables bound in for-loops over stream records and out-of-stream variables are *implicitly* declared using ``var``. (Some examples are shown below.)
|
||||
* Type declarations using `var`, or typed using `num`, `int`, `float`, `str`, and `bool` are necessary to declare local variables. Function arguments and variables bound in for-loops over stream records and out-of-stream variables are *implicitly* declared using `var`. (Some examples are shown below.)
|
||||
|
||||
* Type-checking is done at assignment time. For example, ``float f = 0`` is an error (since ``0`` is an integer), as is ``float f = 0.0; f = 1``. For this reason I prefer to use ``num`` over ``float`` in most contexts since ``num`` encompasses integer and floating-point values. More information about type-checking is at :ref:`reference-dsl-type-checking`.
|
||||
* Type-checking is done at assignment time. For example, `float f = 0` is an error (since `0` is an integer), as is `float f = 0.0; f = 1`. For this reason I prefer to use `num` over `float` in most contexts since `num` encompasses integer and floating-point values. More information is at [Type-checking](reference-dsl-variables.md#type-checking).
|
||||
|
||||
* Bound variables in for-loops over stream records and out-of-stream variables are implicitly local to that block. E.g. in ``for (k, v in $*) { ... }`` ``for ((k1, k2), v in @*) { ... }`` if there are ``k``, ``v``, etc. in the enclosing scope then those will be masked by the loop-local bound variables in the loop, and moreover the values of the loop-local bound variables are not available after the end of the loop.
|
||||
* Bound variables in for-loops over stream records and out-of-stream variables are implicitly local to that block. E.g. in `for (k, v in $*) { ... }` `for ((k1, k2), v in @*) { ... }` if there are `k`, `v`, etc. in the enclosing scope then those will be masked by the loop-local bound variables in the loop, and moreover the values of the loop-local bound variables are not available after the end of the loop.
|
||||
|
||||
* For C-style triple-for loops, if a for-loop variable is defined using ``var``, ``int``, etc. then it is scoped to that for-loop. E.g. ``for (i = 0; i < 10; i += 1) { ... }`` and ``for (int i = 0; i < 10; i += 1) { ... }``. (This is unsurprising.). If there is no typedecl and an outer-scope variable of that name exists, then it is used. (This is also unsurprising.) But of there is no outer-scope variable of that name then the variable is scoped to the for-loop only.
|
||||
* For C-style triple-for loops, if a for-loop variable is defined using `var`, `int`, etc. then it is scoped to that for-loop. E.g. `for (i = 0; i < 10; i += 1) { ... }` and `for (int i = 0; i < 10; i += 1) { ... }`. (This is unsurprising.). If there is no typedecl and an outer-scope variable of that name exists, then it is used. (This is also unsurprising.) But of there is no outer-scope variable of that name then the variable is scoped to the for-loop only.
|
||||
|
||||
The following example demonstrates the scope rules:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/scope-example.mlr</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
func f(a) { # argument is local to the function
|
||||
var b = 100; # local to the function
|
||||
c = 100; # local to the function; does not overwrite outer c
|
||||
|
|
@ -400,15 +442,19 @@ $outer_c = c;
|
|||
$outer_d = d; # there is no outer d defined so no assignment happens
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/scope-example.dat</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
n=1,x=123
|
||||
n=2,x=456
|
||||
n=3,x=789
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --oxtab --from data/scope-example.dat put -f data/scope-example.mlr</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
n 1
|
||||
x 123
|
||||
outer_a 10
|
||||
|
|
@ -434,8 +480,10 @@ outer_c 60
|
|||
|
||||
And this example demonstrates the type-declaration rules:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/type-decl-example.mlr</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
subr s(a, str b, int c) { # a is implicitly var (untyped).
|
||||
# b is explicitly str.
|
||||
# c is explicitly int.
|
||||
|
|
@ -477,11 +525,11 @@ print "outer j =" . j; # j is undefined in this scope
|
|||
|
||||
## Map literals
|
||||
|
||||
Miller's ``put``/``filter`` DSL has four kinds of hashmaps. **Stream records** are (single-level) maps from name to value. **Out-of-stream variables** and **local variables** can also be maps, although they can be multi-level hashmaps (e.g. ``@sum[$x][$y]``). The fourth kind is **map literals**. These cannot be on the left-hand side of assignment expressions. Syntactically they look like JSON, although Miller allows string and integer keys in its map literals while JSON allows only string keys (e.g. ``"3"`` rather than ``3``).
|
||||
Miller's `put`/`filter` DSL has four kinds of hashmaps. **Stream records** are (single-level) maps from name to value. **Out-of-stream variables** and **local variables** can also be maps, although they can be multi-level hashmaps (e.g. `@sum[$x][$y]`). The fourth kind is **map literals**. These cannot be on the left-hand side of assignment expressions. Syntactically they look like JSON, although Miller allows string and integer keys in its map literals while JSON allows only string keys (e.g. `"3"` rather than `3`).
|
||||
|
||||
For example, the following swaps the input stream's ``a`` and ``i`` fields, modifies ``y``, and drops the rest:
|
||||
For example, the following swaps the input stream's `a` and `i` fields, modifies `y`, and drops the rest:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --opprint put '</b>
|
||||
<b> $* = {</b>
|
||||
<b> "a": $i,</b>
|
||||
|
|
@ -489,6 +537,8 @@ For example, the following swaps the input stream's ``a`` and ``i`` fields, modi
|
|||
<b> "y": $y * 10,</b>
|
||||
<b> }</b>
|
||||
<b>' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a i y
|
||||
1 pan 7.268028627434533
|
||||
2 eks 5.221511083334796
|
||||
|
|
@ -499,7 +549,7 @@ a i y
|
|||
|
||||
Likewise, you can assign map literals to out-of-stream variables or local variables; pass them as arguments to user-defined functions, return them from functions, and so on:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from data/small put '</b>
|
||||
<b> func f(map m): map {</b>
|
||||
<b> m["x"] *= 200;</b>
|
||||
|
|
@ -507,6 +557,8 @@ Likewise, you can assign map literals to out-of-stream variables or local variab
|
|||
<b> }</b>
|
||||
<b> $* = f({"a": $a, "x": $x});</b>
|
||||
<b>'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,x=69.35802886761648
|
||||
a=eks,x=151.73599295799272
|
||||
a=wye,x=40.92066115326061
|
||||
|
|
@ -516,7 +568,7 @@ a=wye,x=114.65778396040011
|
|||
|
||||
Like out-of-stream and local variables, map literals can be multi-level:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --from data/small put -q '</b>
|
||||
<b> begin {</b>
|
||||
<b> @o = {</b>
|
||||
|
|
@ -536,6 +588,8 @@ Like out-of-stream and local variables, map literals can be multi-level:
|
|||
<b> dump @o;</b>
|
||||
<b> }</b>
|
||||
<b>'</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
{
|
||||
"nrec": 5,
|
||||
"nkey": {
|
||||
|
|
@ -545,25 +599,22 @@ Like out-of-stream and local variables, map literals can be multi-level:
|
|||
}
|
||||
</pre>
|
||||
|
||||
By default, map-valued expressions are dumped using JSON formatting. If you use ``dump`` to print a hashmap with integer keys and you don't want them double-quoted (JSON-style) then you can use ``mlr put --jknquoteint``. See also ``mlr put --help``.
|
||||
|
||||
.. _reference-dsl-type-checking:
|
||||
By default, map-valued expressions are dumped using JSON formatting. If you use `dump` to print a hashmap with integer keys and you don't want them double-quoted (JSON-style) then you can use `mlr put --jknquoteint`. See also `mlr put --help`.
|
||||
|
||||
## Type-checking
|
||||
|
||||
Miller's ``put``/``filter`` DSLs support two optional kinds of type-checking. One is inline **type-tests** and **type-assertions** within expressions. The other is **type declarations** for assignments to local variables, binding of arguments to user-defined functions, and return values from user-defined functions, These are discussed in the following subsections.
|
||||
Miller's `put`/`filter` DSLs support two optional kinds of type-checking. One is inline **type-tests** and **type-assertions** within expressions. The other is **type declarations** for assignments to local variables, binding of arguments to user-defined functions, and return values from user-defined functions, These are discussed in the following subsections.
|
||||
|
||||
Use of type-checking is entirely up to you: omit it if you want flexibility with heterogeneous data; use it if you want to help catch misspellings in your DSL code or unexpected irregularities in your input data.
|
||||
|
||||
.. _reference-dsl-type-tests-and-assertions:
|
||||
### Type-test and type-assertion expressions
|
||||
|
||||
Type-test and type-assertion expressions
|
||||
................................................................
|
||||
The following `is...` functions take a value and return a boolean indicating whether the argument is of the indicated type. The `assert_...` functions return their argument if it is of the specified type, and cause a fatal error otherwise:
|
||||
|
||||
The following ``is...`` functions take a value and return a boolean indicating whether the argument is of the indicated type. The ``assert_...`` functions return their argument if it is of the specified type, and cause a fatal error otherwise:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr -f | grep ^is</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
is_absent
|
||||
is_array
|
||||
is_bool
|
||||
|
|
@ -585,8 +636,10 @@ is_present
|
|||
is_string
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr -f | grep ^assert</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
asserting_absent
|
||||
asserting_array
|
||||
asserting_bool
|
||||
|
|
@ -610,25 +663,24 @@ asserting_string
|
|||
|
||||
See [Data-cleaning Examples](data-cleaning-examples.md) for examples of how to use these.
|
||||
|
||||
Type-declarations for local variables, function parameter, and function return values
|
||||
...............................................................................................
|
||||
### Type-declarations for local variables, function parameter, and function return values
|
||||
|
||||
Local variables can be defined either untyped as in ``x = 1``, or typed as in ``int x = 1``. Types include **var** (explicitly untyped), **int**, **float**, **num** (int or float), **str**, **bool**, and **map**. These optional type declarations are enforced at the time values are assigned to variables: whether at the initial value assignment as in ``int x = 1`` or in any subsequent assignments to the same variable farther down in the scope.
|
||||
Local variables can be defined either untyped as in `x = 1`, or typed as in `int x = 1`. Types include **var** (explicitly untyped), **int**, **float**, **num** (int or float), **str**, **bool**, and **map**. These optional type declarations are enforced at the time values are assigned to variables: whether at the initial value assignment as in `int x = 1` or in any subsequent assignments to the same variable farther down in the scope.
|
||||
|
||||
The reason for ``num`` is that ``int`` and ``float`` typedecls are very precise:
|
||||
The reason for `num` is that `int` and `float` typedecls are very precise:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
float a = 0; # Runtime error since 0 is int not float
|
||||
int b = 1.0; # Runtime error since 1.0 is float not int
|
||||
num c = 0; # OK
|
||||
num d = 1.0; # OK
|
||||
</pre>
|
||||
|
||||
A suggestion is to use ``num`` for general use when you want numeric content, and use ``int`` when you genuinely want integer-only values, e.g. in loop indices or map keys (since Miller map keys can only be strings or ints).
|
||||
A suggestion is to use `num` for general use when you want numeric content, and use `int` when you genuinely want integer-only values, e.g. in loop indices or map keys (since Miller map keys can only be strings or ints).
|
||||
|
||||
The ``var`` type declaration indicates no type restrictions, e.g. ``var x = 1`` has the same type restrictions on ``x`` as ``x = 1``. The difference is in intentional shadowing: if you have ``x = 1`` in outer scope and ``x = 2`` in inner scope (e.g. within a for-loop or an if-statement) then outer-scope ``x`` has value 2 after the second assignment. But if you have ``var x = 2`` in the inner scope, then you are declaring a variable scoped to the inner block.) For example:
|
||||
The `var` type declaration indicates no type restrictions, e.g. `var x = 1` has the same type restrictions on `x` as `x = 1`. The difference is in intentional shadowing: if you have `x = 1` in outer scope and `x = 2` in inner scope (e.g. within a for-loop or an if-statement) then outer-scope `x` has value 2 after the second assignment. But if you have `var x = 2` in the inner scope, then you are declaring a variable scoped to the inner block.) For example:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
x = 1;
|
||||
if (NR == 4) {
|
||||
x = 2; # Refers to outer-scope x: value changes from 1 to 2.
|
||||
|
|
@ -636,7 +688,7 @@ if (NR == 4) {
|
|||
print x; # Value of x is now two
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
x = 1;
|
||||
if (NR == 4) {
|
||||
var x = 2; # Defines a new inner-scope x with value 2
|
||||
|
|
@ -646,7 +698,7 @@ print x; # Value of this x is still 1
|
|||
|
||||
Likewise function arguments can optionally be typed, with type enforced when the function is called:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
func f(map m, int i) {
|
||||
...
|
||||
}
|
||||
|
|
@ -659,9 +711,9 @@ if (NR == 4) {
|
|||
print x; # Value of this x is still 1
|
||||
</pre>
|
||||
|
||||
Thirdly, function return values can be type-checked at the point of ``return`` using ``:`` and a typedecl after the parameter list:
|
||||
Thirdly, function return values can be type-checked at the point of `return` using `:` and a typedecl after the parameter list:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
func f(map m, int i): bool {
|
||||
...
|
||||
...
|
||||
|
|
@ -688,7 +740,7 @@ Please see [xxxx](reference-main-null-data.md).
|
|||
|
||||
## Aggregate variable assignments
|
||||
|
||||
There are three remaining kinds of variable assignment using out-of-stream variables, the last two of which use the ``$*`` syntax:
|
||||
There are three remaining kinds of variable assignment using out-of-stream variables, the last two of which use the `$*` syntax:
|
||||
|
||||
* Recursive copy of out-of-stream variables
|
||||
* Out-of-stream variable assigned to full stream record
|
||||
|
|
@ -696,8 +748,10 @@ There are three remaining kinds of variable assignment using out-of-stream varia
|
|||
|
||||
Example recursive copy of out-of-stream variables:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --opprint put -q '@v["sum"] += $x; @v["count"] += 1; end{dump; @w = @v; dump}' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
{
|
||||
"v": {
|
||||
"sum": 2.264761728567491,
|
||||
|
|
@ -718,8 +772,10 @@ Example recursive copy of out-of-stream variables:
|
|||
|
||||
Example of out-of-stream variable assigned to full stream record, where the 2nd record is stashed, and the 4th record is overwritten with that:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put 'NR == 2 {@keep = $*}; NR == 4 {$* = @keep}' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,b=pan,i=1,x=0.3467901443380824,y=0.7268028627434533
|
||||
a=eks,b=pan,i=2,x=0.7586799647899636,y=0.5221511083334797
|
||||
a=wye,b=wye,i=3,x=0.20460330576630303,y=0.33831852551664776
|
||||
|
|
@ -727,10 +783,12 @@ a=eks,b=pan,i=2,x=0.7586799647899636,y=0.5221511083334797
|
|||
a=wye,b=pan,i=5,x=0.5732889198020006,y=0.8636244699032729
|
||||
</pre>
|
||||
|
||||
Example of full stream record assigned to an out-of-stream variable, finding the record for which the ``x`` field has the largest value in the input stream:
|
||||
Example of full stream record assigned to an out-of-stream variable, finding the record for which the `x` field has the largest value in the input stream:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,b=pan,i=1,x=0.3467901443380824,y=0.7268028627434533
|
||||
a=eks,b=pan,i=2,x=0.7586799647899636,y=0.5221511083334797
|
||||
a=wye,b=wye,i=3,x=0.20460330576630303,y=0.33831852551664776
|
||||
|
|
@ -738,19 +796,23 @@ a=eks,b=wye,i=4,x=0.38139939387114097,y=0.13418874328430463
|
|||
a=wye,b=pan,i=5,x=0.5732889198020006,y=0.8636244699032729
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr --opprint put -q '</b>
|
||||
<b> is_null(@xmax) || $x > @xmax {@xmax=$x; @recmax=$*};</b>
|
||||
<b> end {emit @recmax}</b>
|
||||
<b>' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a b i x y
|
||||
eks pan 2 0.7586799647899636 0.5221511083334797
|
||||
</pre>
|
||||
|
||||
## Keywords for filter and put
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr help usage-keywords</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
all: used in "emit", "emitp", and "unset" as a synonym for @*
|
||||
|
||||
begin: defines a block of statements to be executed before input records
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
Miller has the following kinds of variables:
|
||||
|
||||
**Built-in variables** such as ``NF``, ``NF``, ``FILENAME``, ``M_PI``, and ``M_E``. These are all capital letters and are read-only (although some of them change value from one record to another).
|
||||
**Built-in variables** such as `NF`, `NF`, `FILENAME`, `M_PI`, and `M_E`. These are all capital letters and are read-only (although some of them change value from one record to another).
|
||||
|
||||
**Fields of stream records**, accessed using the ``$`` prefix. These refer to fields of the current data-stream record. For example, in ``echo x=1,y=2 | mlr put '$z = $x + $y'``, ``$x`` and ``$y`` refer to input fields, and ``$z`` refers to a new, computed output field. In a few contexts, presented below, you can refer to the entire record as ``$*``.
|
||||
**Fields of stream records**, accessed using the `$` prefix. These refer to fields of the current data-stream record. For example, in `echo x=1,y=2 | mlr put '$z = $x + $y'`, `$x` and `$y` refer to input fields, and `$z` refers to a new, computed output field. In a few contexts, presented below, you can refer to the entire record as `$*`.
|
||||
|
||||
**Out-of-stream variables** accessed using the ``@`` prefix. These refer to data which persist from one record to the next, including in ``begin`` and ``end`` blocks (which execute before/after the record stream is consumed, respectively). You use them to remember values across records, such as sums, differences, counters, and so on. In a few contexts, presented below, you can refer to the entire out-of-stream-variables collection as ``@*``.
|
||||
**Out-of-stream variables** accessed using the `@` prefix. These refer to data which persist from one record to the next, including in `begin` and `end` blocks (which execute before/after the record stream is consumed, respectively). You use them to remember values across records, such as sums, differences, counters, and so on. In a few contexts, presented below, you can refer to the entire out-of-stream-variables collection as `@*`.
|
||||
|
||||
**Local variables** are limited in scope and extent to the current statements being executed: these include function arguments, bound variables in for loops, and explicitly declared local variables.
|
||||
|
||||
|
|
@ -14,9 +14,9 @@ Miller has the following kinds of variables:
|
|||
|
||||
## Built-in variables
|
||||
|
||||
These are written all in capital letters, such as ``NR``, ``NF``, ``FILENAME``, and only a small, specific set of them is defined by Miller.
|
||||
These are written all in capital letters, such as `NR`, `NF`, `FILENAME`, and only a small, specific set of them is defined by Miller.
|
||||
|
||||
Namely, Miller supports the following five built-in variables for :doc:`filter and put <reference-dsl>`, all ``awk``-inspired: ``NF``, ``NR``, ``FNR``, ``FILENUM``, and ``FILENAME``, as well as the mathematical constants ``M_PI`` and ``M_E``. Lastly, the ``ENV`` hashmap allows read access to environment variables, e.g. ``ENV["HOME"]`` or ``ENV["foo_".$hostname]``.
|
||||
Namely, Miller supports the following five built-in variables for [filter and put](reference-dsl.md), all `awk`-inspired: `NF`, `NR`, `FNR`, `FILENUM`, and `FILENAME`, as well as the mathematical constants `M_PI` and `M_E`. Lastly, the `ENV` hashmap allows read access to environment variables, e.g. `ENV["HOME"]` or `ENV["foo_".$hostname]`.
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr filter 'FNR == 2' data/small*
|
||||
|
|
@ -26,9 +26,9 @@ GENMD_RUN_COMMAND
|
|||
mlr put '$fnr = FNR' data/small*
|
||||
GENMD_EOF
|
||||
|
||||
Their values of ``NF``, ``NR``, ``FNR``, ``FILENUM``, and ``FILENAME`` change from one record to the next as Miller scans through your input data stream. The mathematical constants, of course, do not change; ``ENV`` is populated from the system environment variables at the time Miller starts and is read-only for the remainder of program execution.
|
||||
Their values of `NF`, `NR`, `FNR`, `FILENUM`, and `FILENAME` change from one record to the next as Miller scans through your input data stream. The mathematical constants, of course, do not change; `ENV` is populated from the system environment variables at the time Miller starts and is read-only for the remainder of program execution.
|
||||
|
||||
Their **scope is global**: you can refer to them in any ``filter`` or ``put`` statement. Their values are assigned by the input-record reader:
|
||||
Their **scope is global**: you can refer to them in any `filter` or `put` statement. Their values are assigned by the input-record reader:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr --csv put '$nr = NR' data/a.csv
|
||||
|
|
@ -38,15 +38,15 @@ GENMD_RUN_COMMAND
|
|||
mlr --csv repeat -n 3 then put '$nr = NR' data/a.csv
|
||||
GENMD_EOF
|
||||
|
||||
The **extent** is for the duration of the put/filter: in a ``begin`` statement (which executes before the fimd.input record is consumed) you will find ``NR=1`` and in an ``end`` statement (which is executed after the last input record is consumed) you will find ``NR`` to be the total number of records ingested.
|
||||
The **extent** is for the duration of the put/filter: in a `begin` statement (which executes before the fimd.input record is consumed) you will find `NR=1` and in an `end` statement (which is executed after the last input record is consumed) you will find `NR` to be the total number of records ingested.
|
||||
|
||||
These are all **read-only** for the ``mlr put`` and ``mlr filter`` DSLs: they may be assigned from, e.g. ``$nr=NR``, but they may not be assigned to: ``NR=100`` is a syntax error.
|
||||
These are all **read-only** for the `mlr put` and `mlr filter` DSLs: they may be assigned from, e.g. `$nr=NR`, but they may not be assigned to: `NR=100` is a syntax error.
|
||||
|
||||
## Field names
|
||||
|
||||
Names of fields within stream records must be specified using a ``$`` in :doc:`filter and put expressions <reference-dsl>`, even though the dollar signs don't appear in the data stream itself. For integer-indexed data, this looks like ``awk``'s ``$1,$2,$3``, except that Miller allows non-numeric names such as ``$quantity`` or ``$hostname``. Likewise, enclose string literals in double quotes in ``filter`` expressions even though they don't appear in file data. In particular, ``mlr filter '$x=="abc"'`` passes through the record ``x=abc``.
|
||||
Names of fields within stream records must be specified using a `$` in [filter and put expressions](reference-dsl.md), even though the dollar signs don't appear in the data stream itself. For integer-indexed data, this looks like `awk`'s `$1,$2,$3`, except that Miller allows non-numeric names such as `$quantity` or `$hostname`. Likewise, enclose string literals in double quotes in `filter` expressions even though they don't appear in file data. In particular, `mlr filter '$x=="abc"'` passes through the record `x=abc`.
|
||||
|
||||
If field names have **special characters** such as ``.`` then you can use braces, e.g. ``'${field.name}'``.
|
||||
If field names have **special characters** such as `.` then you can use braces, e.g. `'${field.name}'`.
|
||||
|
||||
You may also use a **computed field name** in square brackets, e.g.
|
||||
|
||||
|
|
@ -62,19 +62,19 @@ Notes:
|
|||
|
||||
The names of record fields depend on the contents of your input data stream, and their values change from one record to the next as Miller scans through your input data stream.
|
||||
|
||||
Their **extent** is limited to the current record; their **scope** is the ``filter`` or ``put`` command in which they appear.
|
||||
Their **extent** is limited to the current record; their **scope** is the `filter` or `put` command in which they appear.
|
||||
|
||||
These are **read-write**: you can do ``$y=2*$x``, ``$x=$x+1``, etc.
|
||||
These are **read-write**: you can do `$y=2*$x`, `$x=$x+1`, etc.
|
||||
|
||||
Records are Miller's output: field names present in the input stream are passed through to output (written to standard output) unless fields are removed with ``cut``, or records are excluded with ``filter`` or ``put -q``, etc. Simply assign a value to a field and it will be output.
|
||||
Records are Miller's output: field names present in the input stream are passed through to output (written to standard output) unless fields are removed with `cut`, or records are excluded with `filter` or `put -q`, etc. Simply assign a value to a field and it will be output.
|
||||
|
||||
## Positional field names
|
||||
|
||||
Even though Miller's main selling point is name-indexing, sometimes you really want to refer to a field name by its positional index (starting from 1).
|
||||
|
||||
Use ``$[[3]]`` to access the name of field 3. More generally, any expression evaluating to an integer can go between ``$[[`` and ``]]``.
|
||||
Use `$[[3]]` to access the name of field 3. More generally, any expression evaluating to an integer can go between `$[[` and `]]`.
|
||||
|
||||
Then using a computed field name, ``$[ $[[3]] ]`` is the value in the third field. This has the shorter equivalent notation ``$[[[3]]]``.
|
||||
Then using a computed field name, `$[ $[[3]] ]` is the value in the third field. This has the shorter equivalent notation `$[[[3]]]`.
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr cat data/small
|
||||
|
|
@ -100,7 +100,7 @@ GENMD_RUN_COMMAND
|
|||
mlr put '$[[[NR]]] = "NEW"' data/small
|
||||
GENMD_EOF
|
||||
|
||||
Right-hand side accesses to non-existent fields -- i.e. with index less than 1 or greater than ``NF`` -- return an absent value. Likewise, left-hand side accesses only refer to fields which already exist. For example, if a field has 5 records then assigning the name or value of the 6th (or 600th) field results in a no-op.
|
||||
Right-hand side accesses to non-existent fields -- i.e. with index less than 1 or greater than `NF` -- return an absent value. Likewise, left-hand side accesses only refer to fields which already exist. For example, if a field has 5 records then assigning the name or value of the 6th (or 600th) field results in a no-op.
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr put '$[[6]] = "NEW"' data/small
|
||||
|
|
@ -112,11 +112,11 @@ GENMD_EOF
|
|||
|
||||
## Out-of-stream variables
|
||||
|
||||
These are prefixed with an at-sign, e.g. ``@sum``. Furthermore, unlike built-in variables and stream-record fields, they are maintained in an arbitrarily nested hashmap: you can do ``@sum += $quanity``, or ``@sum[$color] += $quanity``, or ``@sum[$color][$shape] += $quanity``. The keys for the multi-level hashmap can be any expression which evaluates to string or integer: e.g. ``@sum[NR] = $a + $b``, ``@sum[$a."-".$b] = $x``, etc.
|
||||
These are prefixed with an at-sign, e.g. `@sum`. Furthermore, unlike built-in variables and stream-record fields, they are maintained in an arbitrarily nested hashmap: you can do `@sum += $quanity`, or `@sum[$color] += $quanity`, or `@sum[$color][$shape] += $quanity`. The keys for the multi-level hashmap can be any expression which evaluates to string or integer: e.g. `@sum[NR] = $a + $b`, `@sum[$a."-".$b] = $x`, etc.
|
||||
|
||||
Their names and their values are entirely under your control; they change only when you assign to them.
|
||||
|
||||
Just as for field names in stream records, if you want to define out-of-stream variables with **special characters** such as ``.`` then you can use braces, e.g. ``'@{variable.name}["index"]'``.
|
||||
Just as for field names in stream records, if you want to define out-of-stream variables with **special characters** such as `.` then you can use braces, e.g. `'@{variable.name}["index"]'`.
|
||||
|
||||
You may use a **computed key** in square brackets, e.g.
|
||||
|
||||
|
|
@ -124,7 +124,7 @@ GENMD_RUN_COMMAND
|
|||
echo s=green,t=blue,a=3,b=4 | mlr put -q '@[$s."_".$t] = $a * $b; emit all'
|
||||
GENMD_EOF
|
||||
|
||||
Out-of-stream variables are **scoped** to the ``put`` command in which they appear. In particular, if you have two or more ``put`` commands separated by ``then``, each put will have its own set of out-of-stream variables:
|
||||
Out-of-stream variables are **scoped** to the `put` command in which they appear. In particular, if you have two or more `put` commands separated by `then`, each put will have its own set of out-of-stream variables:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
cat data/a.dkvp
|
||||
|
|
@ -136,13 +136,13 @@ mlr put '@sum += $a; end {emit @sum}' \
|
|||
data/a.dkvp
|
||||
GENMD_EOF
|
||||
|
||||
Out-of-stream variables' **extent** is from the start to the end of the record stream, i.e. every time the ``put`` or ``filter`` statement referring to them is executed.
|
||||
Out-of-stream variables' **extent** is from the start to the end of the record stream, i.e. every time the `put` or `filter` statement referring to them is executed.
|
||||
|
||||
Out-of-stream variables are **read-write**: you can do ``$sum=@sum``, ``@sum=$sum``, etc.
|
||||
Out-of-stream variables are **read-write**: you can do `$sum=@sum`, `@sum=$sum`, etc.
|
||||
|
||||
## Indexed out-of-stream variables
|
||||
|
||||
Using an index on the ``@count`` and ``@sum`` variables, we get the benefit of the ``-g`` (group-by) option which ``mlr stats1`` and various other Miller commands have:
|
||||
Using an index on the `@count` and `@sum` variables, we get the benefit of the `-g` (group-by) option which `mlr stats1` and various other Miller commands have:
|
||||
|
||||
GENMD_INCLUDE_AND_RUN_ESCAPED(data/begin-end-example-6.sh)
|
||||
|
||||
|
|
@ -152,17 +152,15 @@ Indices can be arbitrarily deep -- here there are two or more of them:
|
|||
|
||||
GENMD_INCLUDE_AND_RUN_ESCAPED(data/begin-end-example-6a.sh)
|
||||
|
||||
The idea is that ``stats1``, and other Miller verbs, encapsulate frequently-used patterns with a minimum of keystroking (and run a little faster), whereas using out-of-stream variables you have more flexibility and control in what you do.
|
||||
The idea is that `stats1`, and other Miller verbs, encapsulate frequently-used patterns with a minimum of keystroking (and run a little faster), whereas using out-of-stream variables you have more flexibility and control in what you do.
|
||||
|
||||
Begin/end blocks can be mixed with pattern/action blocks. For example:
|
||||
|
||||
GENMD_INCLUDE_AND_RUN_ESCAPED(data/begin-end-example-8.sh)
|
||||
|
||||
.. _reference-dsl-local-variables:
|
||||
|
||||
## Local variables
|
||||
|
||||
Local variables are similar to out-of-stream variables, except that their extent is limited to the expressions in which they appear (and their basenames can't be computed using square brackets). There are three kinds of local variables: **arguments** to functions/subroutines, **variables bound within for-loops**, and **locals** defined within control blocks. They may be untyped using ``var``, or typed using ``num``, ``int``, ``float``, ``str``, ``bool``, and ``map``.
|
||||
Local variables are similar to out-of-stream variables, except that their extent is limited to the expressions in which they appear (and their basenames can't be computed using square brackets). There are three kinds of local variables: **arguments** to functions/subroutines, **variables bound within for-loops**, and **locals** defined within control blocks. They may be untyped using `var`, or typed using `num`, `int`, `float`, `str`, `bool`, and `map`.
|
||||
|
||||
For example:
|
||||
|
||||
|
|
@ -170,27 +168,27 @@ GENMD_INCLUDE_AND_RUN_ESCAPED(data/local-example-1.sh)
|
|||
|
||||
Things which are completely unsurprising, resembling many other languages:
|
||||
|
||||
* Parameter names are bound to their arguments but can be reassigned, e.g. if there is a parameter named ``a`` then you can reassign the value of ``a`` to be something else within the function if you like.
|
||||
* Parameter names are bound to their arguments but can be reassigned, e.g. if there is a parameter named `a` then you can reassign the value of `a` to be something else within the function if you like.
|
||||
|
||||
* However, you cannot redeclare the *type* of an argument or a local: ``var a=1; var a=2`` is an error but ``var a=1; a=2`` is OK.
|
||||
* However, you cannot redeclare the *type* of an argument or a local: `var a=1; var a=2` is an error but `var a=1; a=2` is OK.
|
||||
|
||||
* All argument-passing is positional rather than by name; arguments are passed by value, not by reference. (This is also true for map-valued variables: they are not, and cannot be, passed by reference)
|
||||
|
||||
* You can define locals (using ``var``, ``num``, etc.) at any scope (if-statements, else-statements, while-loops, for-loops, or the top-level scope), and nested scopes will have access (more details on scope in the next section). If you define a local variable with the same name inside an inner scope, then a new variable is created with the narrower scope.
|
||||
* You can define locals (using `var`, `num`, etc.) at any scope (if-statements, else-statements, while-loops, for-loops, or the top-level scope), and nested scopes will have access (more details on scope in the next section). If you define a local variable with the same name inside an inner scope, then a new variable is created with the narrower scope.
|
||||
|
||||
* If you assign to a local variable for the first time in a scope without declaring it as ``var``, ``num``, etc. then: if it exists in an outer scope, that outer-scope variable will be updated; if not, it will be defined in the current scope as if ``var`` had been used. (See also :ref:`reference-dsl-type-checking` for an example.) I recommend always declaring variables explicitly to make the intended scoping clear.
|
||||
* If you assign to a local variable for the first time in a scope without declaring it as `var`, `num`, etc. then: if it exists in an outer scope, that outer-scope variable will be updated; if not, it will be defined in the current scope as if `var` had been used. (See also [Type-checking](reference-dsl-variables.md#type-checking) for an example.) I recommend always declaring variables explicitly to make the intended scoping clear.
|
||||
|
||||
* Functions and subroutines never have access to locals from their callee (unless passed by value as arguments).
|
||||
|
||||
Things which are perhaps surprising compared to other languages:
|
||||
|
||||
* Type declarations using ``var``, or typed using ``num``, ``int``, ``float``, ``str``, and ``bool`` are necessary to declare local variables. Function arguments and variables bound in for-loops over stream records and out-of-stream variables are *implicitly* declared using ``var``. (Some examples are shown below.)
|
||||
* Type declarations using `var`, or typed using `num`, `int`, `float`, `str`, and `bool` are necessary to declare local variables. Function arguments and variables bound in for-loops over stream records and out-of-stream variables are *implicitly* declared using `var`. (Some examples are shown below.)
|
||||
|
||||
* Type-checking is done at assignment time. For example, ``float f = 0`` is an error (since ``0`` is an integer), as is ``float f = 0.0; f = 1``. For this reason I prefer to use ``num`` over ``float`` in most contexts since ``num`` encompasses integer and floating-point values. More information about type-checking is at :ref:`reference-dsl-type-checking`.
|
||||
* Type-checking is done at assignment time. For example, `float f = 0` is an error (since `0` is an integer), as is `float f = 0.0; f = 1`. For this reason I prefer to use `num` over `float` in most contexts since `num` encompasses integer and floating-point values. More information is at [Type-checking](reference-dsl-variables.md#type-checking).
|
||||
|
||||
* Bound variables in for-loops over stream records and out-of-stream variables are implicitly local to that block. E.g. in ``for (k, v in $*) { ... }`` ``for ((k1, k2), v in @*) { ... }`` if there are ``k``, ``v``, etc. in the enclosing scope then those will be masked by the loop-local bound variables in the loop, and moreover the values of the loop-local bound variables are not available after the end of the loop.
|
||||
* Bound variables in for-loops over stream records and out-of-stream variables are implicitly local to that block. E.g. in `for (k, v in $*) { ... }` `for ((k1, k2), v in @*) { ... }` if there are `k`, `v`, etc. in the enclosing scope then those will be masked by the loop-local bound variables in the loop, and moreover the values of the loop-local bound variables are not available after the end of the loop.
|
||||
|
||||
* For C-style triple-for loops, if a for-loop variable is defined using ``var``, ``int``, etc. then it is scoped to that for-loop. E.g. ``for (i = 0; i < 10; i += 1) { ... }`` and ``for (int i = 0; i < 10; i += 1) { ... }``. (This is unsurprising.). If there is no typedecl and an outer-scope variable of that name exists, then it is used. (This is also unsurprising.) But of there is no outer-scope variable of that name then the variable is scoped to the for-loop only.
|
||||
* For C-style triple-for loops, if a for-loop variable is defined using `var`, `int`, etc. then it is scoped to that for-loop. E.g. `for (i = 0; i < 10; i += 1) { ... }` and `for (int i = 0; i < 10; i += 1) { ... }`. (This is unsurprising.). If there is no typedecl and an outer-scope variable of that name exists, then it is used. (This is also unsurprising.) But of there is no outer-scope variable of that name then the variable is scoped to the for-loop only.
|
||||
|
||||
The following example demonstrates the scope rules:
|
||||
|
||||
|
|
@ -214,9 +212,9 @@ GENMD_EOF
|
|||
|
||||
## Map literals
|
||||
|
||||
Miller's ``put``/``filter`` DSL has four kinds of hashmaps. **Stream records** are (single-level) maps from name to value. **Out-of-stream variables** and **local variables** can also be maps, although they can be multi-level hashmaps (e.g. ``@sum[$x][$y]``). The fourth kind is **map literals**. These cannot be on the left-hand side of assignment expressions. Syntactically they look like JSON, although Miller allows string and integer keys in its map literals while JSON allows only string keys (e.g. ``"3"`` rather than ``3``).
|
||||
Miller's `put`/`filter` DSL has four kinds of hashmaps. **Stream records** are (single-level) maps from name to value. **Out-of-stream variables** and **local variables** can also be maps, although they can be multi-level hashmaps (e.g. `@sum[$x][$y]`). The fourth kind is **map literals**. These cannot be on the left-hand side of assignment expressions. Syntactically they look like JSON, although Miller allows string and integer keys in its map literals while JSON allows only string keys (e.g. `"3"` rather than `3`).
|
||||
|
||||
For example, the following swaps the input stream's ``a`` and ``i`` fields, modifies ``y``, and drops the rest:
|
||||
For example, the following swaps the input stream's `a` and `i` fields, modifies `y`, and drops the rest:
|
||||
|
||||
GENMD_INCLUDE_AND_RUN_ESCAPED(data/map-literal-example-1.sh)
|
||||
|
||||
|
|
@ -228,22 +226,17 @@ Like out-of-stream and local variables, map literals can be multi-level:
|
|||
|
||||
GENMD_INCLUDE_AND_RUN_ESCAPED(data/map-literal-example-3.sh)
|
||||
|
||||
By default, map-valued expressions are dumped using JSON formatting. If you use ``dump`` to print a hashmap with integer keys and you don't want them double-quoted (JSON-style) then you can use ``mlr put --jknquoteint``. See also ``mlr put --help``.
|
||||
|
||||
.. _reference-dsl-type-checking:
|
||||
By default, map-valued expressions are dumped using JSON formatting. If you use `dump` to print a hashmap with integer keys and you don't want them double-quoted (JSON-style) then you can use `mlr put --jknquoteint`. See also `mlr put --help`.
|
||||
|
||||
## Type-checking
|
||||
|
||||
Miller's ``put``/``filter`` DSLs support two optional kinds of type-checking. One is inline **type-tests** and **type-assertions** within expressions. The other is **type declarations** for assignments to local variables, binding of arguments to user-defined functions, and return values from user-defined functions, These are discussed in the following subsections.
|
||||
Miller's `put`/`filter` DSLs support two optional kinds of type-checking. One is inline **type-tests** and **type-assertions** within expressions. The other is **type declarations** for assignments to local variables, binding of arguments to user-defined functions, and return values from user-defined functions, These are discussed in the following subsections.
|
||||
|
||||
Use of type-checking is entirely up to you: omit it if you want flexibility with heterogeneous data; use it if you want to help catch misspellings in your DSL code or unexpected irregularities in your input data.
|
||||
|
||||
.. _reference-dsl-type-tests-and-assertions:
|
||||
### Type-test and type-assertion expressions
|
||||
|
||||
Type-test and type-assertion expressions
|
||||
................................................................
|
||||
|
||||
The following ``is...`` functions take a value and return a boolean indicating whether the argument is of the indicated type. The ``assert_...`` functions return their argument if it is of the specified type, and cause a fatal error otherwise:
|
||||
The following `is...` functions take a value and return a boolean indicating whether the argument is of the indicated type. The `assert_...` functions return their argument if it is of the specified type, and cause a fatal error otherwise:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr -f | grep ^is
|
||||
|
|
@ -255,12 +248,11 @@ GENMD_EOF
|
|||
|
||||
See [Data-cleaning Examples](data-cleaning-examples.md) for examples of how to use these.
|
||||
|
||||
Type-declarations for local variables, function parameter, and function return values
|
||||
...............................................................................................
|
||||
### Type-declarations for local variables, function parameter, and function return values
|
||||
|
||||
Local variables can be defined either untyped as in ``x = 1``, or typed as in ``int x = 1``. Types include **var** (explicitly untyped), **int**, **float**, **num** (int or float), **str**, **bool**, and **map**. These optional type declarations are enforced at the time values are assigned to variables: whether at the initial value assignment as in ``int x = 1`` or in any subsequent assignments to the same variable farther down in the scope.
|
||||
Local variables can be defined either untyped as in `x = 1`, or typed as in `int x = 1`. Types include **var** (explicitly untyped), **int**, **float**, **num** (int or float), **str**, **bool**, and **map**. These optional type declarations are enforced at the time values are assigned to variables: whether at the initial value assignment as in `int x = 1` or in any subsequent assignments to the same variable farther down in the scope.
|
||||
|
||||
The reason for ``num`` is that ``int`` and ``float`` typedecls are very precise:
|
||||
The reason for `num` is that `int` and `float` typedecls are very precise:
|
||||
|
||||
GENMD_CARDIFY
|
||||
float a = 0; # Runtime error since 0 is int not float
|
||||
|
|
@ -269,9 +261,9 @@ num c = 0; # OK
|
|||
num d = 1.0; # OK
|
||||
GENMD_EOF
|
||||
|
||||
A suggestion is to use ``num`` for general use when you want numeric content, and use ``int`` when you genuinely want integer-only values, e.g. in loop indices or map keys (since Miller map keys can only be strings or ints).
|
||||
A suggestion is to use `num` for general use when you want numeric content, and use `int` when you genuinely want integer-only values, e.g. in loop indices or map keys (since Miller map keys can only be strings or ints).
|
||||
|
||||
The ``var`` type declaration indicates no type restrictions, e.g. ``var x = 1`` has the same type restrictions on ``x`` as ``x = 1``. The difference is in intentional shadowing: if you have ``x = 1`` in outer scope and ``x = 2`` in inner scope (e.g. within a for-loop or an if-statement) then outer-scope ``x`` has value 2 after the second assignment. But if you have ``var x = 2`` in the inner scope, then you are declaring a variable scoped to the inner block.) For example:
|
||||
The `var` type declaration indicates no type restrictions, e.g. `var x = 1` has the same type restrictions on `x` as `x = 1`. The difference is in intentional shadowing: if you have `x = 1` in outer scope and `x = 2` in inner scope (e.g. within a for-loop or an if-statement) then outer-scope `x` has value 2 after the second assignment. But if you have `var x = 2` in the inner scope, then you are declaring a variable scoped to the inner block.) For example:
|
||||
|
||||
GENMD_CARDIFY
|
||||
x = 1;
|
||||
|
|
@ -304,7 +296,7 @@ if (NR == 4) {
|
|||
print x; # Value of this x is still 1
|
||||
GENMD_EOF
|
||||
|
||||
Thirdly, function return values can be type-checked at the point of ``return`` using ``:`` and a typedecl after the parameter list:
|
||||
Thirdly, function return values can be type-checked at the point of `return` using `:` and a typedecl after the parameter list:
|
||||
|
||||
GENMD_CARDIFY
|
||||
func f(map m, int i): bool {
|
||||
|
|
@ -333,7 +325,7 @@ Please see [xxxx](reference-main-null-data.md).
|
|||
|
||||
## Aggregate variable assignments
|
||||
|
||||
There are three remaining kinds of variable assignment using out-of-stream variables, the last two of which use the ``$*`` syntax:
|
||||
There are three remaining kinds of variable assignment using out-of-stream variables, the last two of which use the `$*` syntax:
|
||||
|
||||
* Recursive copy of out-of-stream variables
|
||||
* Out-of-stream variable assigned to full stream record
|
||||
|
|
@ -351,7 +343,7 @@ GENMD_RUN_COMMAND
|
|||
mlr put 'NR == 2 {@keep = $*}; NR == 4 {$* = @keep}' data/small
|
||||
GENMD_EOF
|
||||
|
||||
Example of full stream record assigned to an out-of-stream variable, finding the record for which the ``x`` field has the largest value in the input stream:
|
||||
Example of full stream record assigned to an out-of-stream variable, finding the record for which the `x` field has the largest value in the input stream:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
cat data/small
|
||||
|
|
|
|||
|
|
@ -3,12 +3,14 @@
|
|||
|
||||
## Overview
|
||||
|
||||
Here's comparison of verbs and ``put``/``filter`` DSL expressions:
|
||||
Here's comparison of verbs and `put`/`filter` DSL expressions:
|
||||
|
||||
Example:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr stats1 -a sum -f x -g a data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,x_sum=0.3467901443380824
|
||||
a=eks,x_sum=1.1400793586611044
|
||||
a=wye,x_sum=0.7778922255683036
|
||||
|
|
@ -22,8 +24,10 @@ a=wye,x_sum=0.7778922255683036
|
|||
|
||||
Example:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put -q '@x_sum[$a] += $x; end{emit @x_sum, "a"}' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,x_sum=0.3467901443380824
|
||||
a=eks,x_sum=1.1400793586611044
|
||||
a=wye,x_sum=0.7778922255683036
|
||||
|
|
@ -35,12 +39,14 @@ a=wye,x_sum=0.7778922255683036
|
|||
* There is more to learn
|
||||
* They are highly customizable
|
||||
|
||||
Please see [Verbs Reference](reference-verbs.md) for information on verbs other than ``put`` and ``filter``.
|
||||
Please see [Verbs Reference](reference-verbs.md) for information on verbs other than `put` and `filter`.
|
||||
|
||||
The essential usages of ``mlr filter`` and ``mlr put`` are for record-selection and record-updating expressions, respectively. For example, given the following input data:
|
||||
The essential usages of `mlr filter` and `mlr put` are for record-selection and record-updating expressions, respectively. For example, given the following input data:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>cat data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,b=pan,i=1,x=0.3467901443380824,y=0.7268028627434533
|
||||
a=eks,b=pan,i=2,x=0.7586799647899636,y=0.5221511083334797
|
||||
a=wye,b=wye,i=3,x=0.20460330576630303,y=0.33831852551664776
|
||||
|
|
@ -48,18 +54,22 @@ a=eks,b=wye,i=4,x=0.38139939387114097,y=0.13418874328430463
|
|||
a=wye,b=pan,i=5,x=0.5732889198020006,y=0.8636244699032729
|
||||
</pre>
|
||||
|
||||
you might retain only the records whose ``a`` field has value ``eks``:
|
||||
you might retain only the records whose `a` field has value `eks`:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr filter '$a == "eks"' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=eks,b=pan,i=2,x=0.7586799647899636,y=0.5221511083334797
|
||||
a=eks,b=wye,i=4,x=0.38139939387114097,y=0.13418874328430463
|
||||
</pre>
|
||||
|
||||
or you might add a new field which is a function of existing fields:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr put '$ab = $a . "_" . $b ' data/small</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
a=pan,b=pan,i=1,x=0.3467901443380824,y=0.7268028627434533,ab=pan_pan
|
||||
a=eks,b=pan,i=2,x=0.7586799647899636,y=0.5221511083334797,ab=eks_pan
|
||||
a=wye,b=wye,i=3,x=0.20460330576630303,y=0.33831852551664776,ab=wye_wye
|
||||
|
|
@ -67,13 +77,13 @@ a=eks,b=wye,i=4,x=0.38139939387114097,y=0.13418874328430463,ab=eks_wye
|
|||
a=wye,b=pan,i=5,x=0.5732889198020006,y=0.8636244699032729,ab=wye_pan
|
||||
</pre>
|
||||
|
||||
The two verbs ``mlr filter`` and ``mlr put`` are essentially the same. The only differences are:
|
||||
The two verbs `mlr filter` and `mlr put` are essentially the same. The only differences are:
|
||||
|
||||
* Expressions sent to ``mlr filter`` must end with a boolean expression, which is the filtering criterion;
|
||||
* Expressions sent to `mlr filter` must end with a boolean expression, which is the filtering criterion;
|
||||
|
||||
* ``mlr filter`` expressions may not reference the ``filter`` keyword within them; and
|
||||
* `mlr filter` expressions may not reference the `filter` keyword within them; and
|
||||
|
||||
* ``mlr filter`` expressions may not use ``tee``, ``emit``, ``emitp``, or ``emitf``.
|
||||
* `mlr filter` expressions may not use `tee`, `emit`, `emitp`, or `emitf`.
|
||||
|
||||
All the rest is the same: in particular, you can define and invoke functions and subroutines to help produce the final boolean statement, and record fields may be assigned to in the statements preceding the final boolean statement.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
## Overview
|
||||
|
||||
Here's comparison of verbs and ``put``/``filter`` DSL expressions:
|
||||
Here's comparison of verbs and `put`/`filter` DSL expressions:
|
||||
|
||||
Example:
|
||||
|
||||
|
|
@ -28,15 +28,15 @@ GENMD_EOF
|
|||
* There is more to learn
|
||||
* They are highly customizable
|
||||
|
||||
Please see [Verbs Reference](reference-verbs.md) for information on verbs other than ``put`` and ``filter``.
|
||||
Please see [Verbs Reference](reference-verbs.md) for information on verbs other than `put` and `filter`.
|
||||
|
||||
The essential usages of ``mlr filter`` and ``mlr put`` are for record-selection and record-updating expressions, respectively. For example, given the following input data:
|
||||
The essential usages of `mlr filter` and `mlr put` are for record-selection and record-updating expressions, respectively. For example, given the following input data:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
cat data/small
|
||||
GENMD_EOF
|
||||
|
||||
you might retain only the records whose ``a`` field has value ``eks``:
|
||||
you might retain only the records whose `a` field has value `eks`:
|
||||
|
||||
GENMD_RUN_COMMAND
|
||||
mlr filter '$a == "eks"' data/small
|
||||
|
|
@ -48,13 +48,13 @@ GENMD_RUN_COMMAND
|
|||
mlr put '$ab = $a . "_" . $b ' data/small
|
||||
GENMD_EOF
|
||||
|
||||
The two verbs ``mlr filter`` and ``mlr put`` are essentially the same. The only differences are:
|
||||
The two verbs `mlr filter` and `mlr put` are essentially the same. The only differences are:
|
||||
|
||||
* Expressions sent to ``mlr filter`` must end with a boolean expression, which is the filtering criterion;
|
||||
* Expressions sent to `mlr filter` must end with a boolean expression, which is the filtering criterion;
|
||||
|
||||
* ``mlr filter`` expressions may not reference the ``filter`` keyword within them; and
|
||||
* `mlr filter` expressions may not reference the `filter` keyword within them; and
|
||||
|
||||
* ``mlr filter`` expressions may not use ``tee``, ``emit``, ``emitp``, or ``emitf``.
|
||||
* `mlr filter` expressions may not use `tee`, `emit`, `emitp`, or `emitf`.
|
||||
|
||||
All the rest is the same: in particular, you can define and invoke functions and subroutines to help produce the final boolean statement, and record fields may be assigned to in the statements preceding the final boolean statement.
|
||||
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
|
||||
## Input scanning
|
||||
|
||||
Numbers in Miller are double-precision float or 64-bit signed integers. Anything scannable as int, e.g ``123`` or ``0xabcd``, is treated as an integer; otherwise, input scannable as float (``4.56`` or ``8e9``) is treated as float; everything else is a string.
|
||||
Numbers in Miller are double-precision float or 64-bit signed integers. Anything scannable as int, e.g `123` or `0xabcd`, is treated as an integer; otherwise, input scannable as float (`4.56` or `8e9`) is treated as float; everything else is a string.
|
||||
|
||||
If you want all numbers to be treated as floats, then you may use ``float()`` in your filter/put expressions (e.g. replacing ``$c = $a * $b`` with ``$c = float($a) * float($b)``) -- or, more simply, use ``mlr filter -F`` and ``mlr put -F`` which forces all numeric input, whether from expression literals or field values, to float. Likewise ``mlr stats1 -F`` and ``mlr step -F`` force integerable accumulators (such as ``count``) to be done in floating-point.
|
||||
If you want all numbers to be treated as floats, then you may use `float()` in your filter/put expressions (e.g. replacing `$c = $a * $b` with `$c = float($a) * float($b)`) -- or, more simply, use `mlr filter -F` and `mlr put -F` which forces all numeric input, whether from expression literals or field values, to float. Likewise `mlr stats1 -F` and `mlr step -F` force integerable accumulators (such as `count`) to be done in floating-point.
|
||||
|
||||
## Conversion by math routines
|
||||
|
||||
For most math functions, integers are cast to float on input, and produce float output: e.g. ``exp(0) = 1.0`` rather than ``1``. The following, however, produce integer output if their inputs are integers: ``+`` ``-`` ``*`` ``/`` ``//`` ``%`` ``abs`` ``ceil`` ``floor`` ``max`` ``min`` ``round`` ``roundm`` ``sgn``. As well, ``stats1 -a min``, ``stats1 -a max``, ``stats1 -a sum``, ``step -a delta``, and ``step -a rsum`` produce integer output if their inputs are integers.
|
||||
For most math functions, integers are cast to float on input, and produce float output: e.g. `exp(0) = 1.0` rather than `1`. The following, however, produce integer output if their inputs are integers: `+` `-` `*` `/` `//` `%` `abs` `ceil` `floor` `max` `min` `round` `roundm` `sgn`. As well, `stats1 -a min`, `stats1 -a max`, `stats1 -a sum`, `step -a delta`, and `step -a rsum` produce integer output if their inputs are integers.
|
||||
|
||||
## Conversion by arithmetic operators
|
||||
|
||||
|
|
@ -19,9 +19,9 @@ The short of it is that Miller does this transparently for you so you needn't th
|
|||
|
||||
Implementation details of this, for the interested: integer adds and subtracts overflow by at most one bit so it suffices to check sign-changes. Thus, Miller allows you to add and subtract arbitrary 64-bit signed integers, converting only to float precisely when the result is less than -2\ :sup:`63` or greater than 2\ :sup:`63`\ -1. Multiplies, on the other hand, can overflow by a word size and a sign-change technique does not suffice to detect overflow. Instead Miller tests whether the floating-point product exceeds the representable integer range. Now, 64-bit integers have 64-bit precision while IEEE-doubles have only 52-bit mantissas -- so, there are 53 bits including implicit leading one. The following experiment explicitly demonstrates the resolution at this range:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-non-highlight">
|
||||
64-bit integer 64-bit integer Casted to double Back to 64-bit
|
||||
in hex in decimal integer
|
||||
in hex in decimal integer
|
||||
0x7ffffffffffff9ff 9223372036854774271 9223372036854773760.000000 0x7ffffffffffff800
|
||||
0x7ffffffffffffa00 9223372036854774272 9223372036854773760.000000 0x7ffffffffffff800
|
||||
0x7ffffffffffffbff 9223372036854774783 9223372036854774784.000000 0x7ffffffffffffc00
|
||||
|
|
@ -38,6 +38,6 @@ That is, one cannot check an integer product to see if it is precisely greater t
|
|||
|
||||
Division and remainder are [pythonic](http://python-history.blogspot.com/2010/08/why-pythons-integer-division-floors.html):
|
||||
|
||||
* Quotient of integers is floating-point: ``7/2`` is ``3.5``.
|
||||
* Integer division is done with ``//``: ``7//2`` is ``3``. This rounds toward the negative.
|
||||
* Quotient of integers is floating-point: `7/2` is `3.5`.
|
||||
* Integer division is done with `//`: `7//2` is `3`. This rounds toward the negative.
|
||||
* Remainders are non-negative.
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
|
||||
## Input scanning
|
||||
|
||||
Numbers in Miller are double-precision float or 64-bit signed integers. Anything scannable as int, e.g ``123`` or ``0xabcd``, is treated as an integer; otherwise, input scannable as float (``4.56`` or ``8e9``) is treated as float; everything else is a string.
|
||||
Numbers in Miller are double-precision float or 64-bit signed integers. Anything scannable as int, e.g `123` or `0xabcd`, is treated as an integer; otherwise, input scannable as float (`4.56` or `8e9`) is treated as float; everything else is a string.
|
||||
|
||||
If you want all numbers to be treated as floats, then you may use ``float()`` in your filter/put expressions (e.g. replacing ``$c = $a * $b`` with ``$c = float($a) * float($b)``) -- or, more simply, use ``mlr filter -F`` and ``mlr put -F`` which forces all numeric input, whether from expression literals or field values, to float. Likewise ``mlr stats1 -F`` and ``mlr step -F`` force integerable accumulators (such as ``count``) to be done in floating-point.
|
||||
If you want all numbers to be treated as floats, then you may use `float()` in your filter/put expressions (e.g. replacing `$c = $a * $b` with `$c = float($a) * float($b)`) -- or, more simply, use `mlr filter -F` and `mlr put -F` which forces all numeric input, whether from expression literals or field values, to float. Likewise `mlr stats1 -F` and `mlr step -F` force integerable accumulators (such as `count`) to be done in floating-point.
|
||||
|
||||
## Conversion by math routines
|
||||
|
||||
For most math functions, integers are cast to float on input, and produce float output: e.g. ``exp(0) = 1.0`` rather than ``1``. The following, however, produce integer output if their inputs are integers: ``+`` ``-`` ``*`` ``/`` ``//`` ``%`` ``abs`` ``ceil`` ``floor`` ``max`` ``min`` ``round`` ``roundm`` ``sgn``. As well, ``stats1 -a min``, ``stats1 -a max``, ``stats1 -a sum``, ``step -a delta``, and ``step -a rsum`` produce integer output if their inputs are integers.
|
||||
For most math functions, integers are cast to float on input, and produce float output: e.g. `exp(0) = 1.0` rather than `1`. The following, however, produce integer output if their inputs are integers: `+` `-` `*` `/` `//` `%` `abs` `ceil` `floor` `max` `min` `round` `roundm` `sgn`. As well, `stats1 -a min`, `stats1 -a max`, `stats1 -a sum`, `step -a delta`, and `step -a rsum` produce integer output if their inputs are integers.
|
||||
|
||||
## Conversion by arithmetic operators
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ Implementation details of this, for the interested: integer adds and subtracts o
|
|||
|
||||
GENMD_CARDIFY
|
||||
64-bit integer 64-bit integer Casted to double Back to 64-bit
|
||||
in hex in decimal integer
|
||||
in hex in decimal integer
|
||||
0x7ffffffffffff9ff 9223372036854774271 9223372036854773760.000000 0x7ffffffffffff800
|
||||
0x7ffffffffffffa00 9223372036854774272 9223372036854773760.000000 0x7ffffffffffff800
|
||||
0x7ffffffffffffbff 9223372036854774783 9223372036854774784.000000 0x7ffffffffffffc00
|
||||
|
|
@ -37,6 +37,6 @@ That is, one cannot check an integer product to see if it is precisely greater t
|
|||
|
||||
Division and remainder are [pythonic](http://python-history.blogspot.com/2010/08/why-pythons-integer-division-floors.html):
|
||||
|
||||
* Quotient of integers is floating-point: ``7/2`` is ``3.5``.
|
||||
* Integer division is done with ``//``: ``7//2`` is ``3``. This rounds toward the negative.
|
||||
* Quotient of integers is floating-point: `7/2` is `3.5`.
|
||||
* Integer division is done with `//`: `7//2` is `3`. This rounds toward the negative.
|
||||
* Remainders are non-negative.
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@
|
|||
|
||||
There are a few nearly-standalone programs which have nothing to do with the rest of Miller, do not participate in record streams, and do not deal with file formats. They might as well be little standalone executables but they're delivered within the main Miller executable for convenience.
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr aux-list</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
Available subcommands:
|
||||
aux-list
|
||||
hex
|
||||
|
|
@ -17,8 +19,10 @@ Available subcommands:
|
|||
For more information, please invoke mlr {subcommand} --help.
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr lecat --help</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
Usage: mlr lecat [options] {zero or more file names}
|
||||
Simply echoes input, but flags CR characters in red and LF characters in green.
|
||||
If zero file names are supplied, standard input is read.
|
||||
|
|
@ -27,8 +31,10 @@ Options:
|
|||
-h or --help: print this message
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr termcvt --help</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
Usage: mlr termcvt [option] {zero or more file names}
|
||||
Option (exactly one is required):
|
||||
--cr2crlf
|
||||
|
|
@ -43,8 +49,10 @@ Zero file names means read from standard input.
|
|||
Output is always to standard output; files are not written in-place.
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr hex --help</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
Usage: mlr hex [options] {zero or more file names}
|
||||
Simple hex-dump.
|
||||
If zero file names are supplied, standard input is read.
|
||||
|
|
@ -53,8 +61,10 @@ Options:
|
|||
-h or --help: print this message
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr unhex --help</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
Usage: mlr unhex [option] {zero or more file names}
|
||||
Options:
|
||||
-h or --help: print this message
|
||||
|
|
@ -64,18 +74,24 @@ Output is always to standard output; files are not written in-place.
|
|||
|
||||
Examples:
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>echo 'Hello, world!' | mlr lecat --mono</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
Hello, world![LF]
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>echo 'Hello, world!' | mlr termcvt --lf2crlf | mlr lecat --mono</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
Hello, world![CR][LF]
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr hex data/budget.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
00000000: 23 20 41 73 61 6e 61 20 2d 2d 20 68 65 72 65 20 |# Asana -- here |
|
||||
00000010: 61 72 65 20 74 68 65 20 62 75 64 67 65 74 20 66 |are the budget f|
|
||||
00000020: 69 67 75 72 65 73 20 79 6f 75 20 61 73 6b 65 64 |igures you asked|
|
||||
|
|
@ -85,8 +101,10 @@ Hello, world![CR][LF]
|
|||
00000060: 72 61 6e 67 65 2c 31 32 33 2e 34 35 0a |range,123.45.|
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr hex -r data/budget.csv</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
23 20 41 73 61 6e 61 20 2d 2d 20 68 65 72 65 20
|
||||
61 72 65 20 74 68 65 20 62 75 64 67 65 74 20 66
|
||||
69 67 75 72 65 73 20 79 6f 75 20 61 73 6b 65 64
|
||||
|
|
@ -96,8 +114,10 @@ Hello, world![CR][LF]
|
|||
72 61 6e 67 65 2c 31 32 33 2e 34 35 0a
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<pre class="pre-highlight">
|
||||
<b>mlr hex -r data/budget.csv | sed 's/20/2a/g' | mlr unhex</b>
|
||||
</pre>
|
||||
<pre class="pre-non-highlight">
|
||||
#*Asana*--*here*are*the*budget*figures*you*asked*for!
|
||||
type,quantity
|
||||
purple,456.78
|
||||
|
|
|
|||
|
|
@ -5,41 +5,41 @@ Miller's input and output are all string-oriented: there is (as of August 2015 a
|
|||
|
||||
Field values are treated as numeric for the following:
|
||||
|
||||
* Numeric sort: ``mlr sort -n``, ``mlr sort -nr``.
|
||||
* Statistics: ``mlr histogram``, ``mlr stats1``, ``mlr stats2``.
|
||||
* Cross-record arithmetic: ``mlr step``.
|
||||
* Numeric sort: `mlr sort -n`, `mlr sort -nr`.
|
||||
* Statistics: `mlr histogram`, `mlr stats1`, `mlr stats2`.
|
||||
* Cross-record arithmetic: `mlr step`.
|
||||
|
||||
For ``mlr put`` and ``mlr filter``:
|
||||
For `mlr put` and `mlr filter`:
|
||||
|
||||
* Miller's types for function processing are **empty-null** (empty string), **absent-null** (reads of unset right-hand sides, or fall-through non-explicit return values from user-defined functions), **error**, **string**, **float** (double-precision), **int** (64-bit signed), and **boolean**.
|
||||
|
||||
* On input, string values representable as numbers, e.g. "3" or "3.1", are treated as int or float, respectively. If a record has ``x=1,y=2`` then ``mlr put '$z=$x+$y'`` will produce ``x=1,y=2,z=3``, and ``mlr put '$z=$x.$y'`` does not give an error simply because the dot operator has been generalized to stringify non-strings. To coerce back to string for processing, use the ``string`` function: ``mlr put '$z=string($x).string($y)'`` will produce ``x=1,y=2,z=12``.
|
||||
* On input, string values representable as numbers, e.g. "3" or "3.1", are treated as int or float, respectively. If a record has `x=1,y=2` then `mlr put '$z=$x+$y'` will produce `x=1,y=2,z=3`, and `mlr put '$z=$x.$y'` does not give an error simply because the dot operator has been generalized to stringify non-strings. To coerce back to string for processing, use the `string` function: `mlr put '$z=string($x).string($y)'` will produce `x=1,y=2,z=12`.
|
||||
|
||||
* On input, string values representable as boolean (e.g. ``"true"``, ``"false"``) are *not* automatically treated as boolean. (This is because ``"true"`` and ``"false"`` are ordinary words, and auto string-to-boolean on a column consisting of words would result in some strings mixed with some booleans.) Use the ``boolean`` function to coerce: e.g. giving the record ``x=1,y=2,w=false`` to ``mlr put '$z=($x<$y) || boolean($w)'``.
|
||||
* On input, string values representable as boolean (e.g. `"true"`, `"false"`) are *not* automatically treated as boolean. (This is because `"true"` and `"false"` are ordinary words, and auto string-to-boolean on a column consisting of words would result in some strings mixed with some booleans.) Use the `boolean` function to coerce: e.g. giving the record `x=1,y=2,w=false` to `mlr put '$z=($x<$y) || boolean($w)'`.
|
||||
|
||||
* Functions take types as described in ``mlr --help-all-functions``: for example, ``log10`` takes float input and produces float output, ``gmt2sec`` maps string to int, and ``sec2gmt`` maps int to string.
|
||||
* Functions take types as described in `mlr --help-all-functions`: for example, `log10` takes float input and produces float output, `gmt2sec` maps string to int, and `sec2gmt` maps int to string.
|
||||
|
||||
* All math functions described in ``mlr --help-all-functions`` take integer as well as float input.
|
||||
* All math functions described in `mlr --help-all-functions` take integer as well as float input.
|
||||
|
||||
## String literals
|
||||
|
||||
You can use the following backslash escapes for strings such as between the double quotes in contexts such as ``mlr filter '$name =~ "..."'``, ``mlr put '$name = $othername . "..."'``, ``mlr put '$name = sub($name, "...", "...")``, etc.:
|
||||
You can use the following backslash escapes for strings such as between the double quotes in contexts such as `mlr filter '$name =~ "..."'`, `mlr put '$name = $othername . "..."'`, `mlr put '$name = sub($name, "...", "...")`, etc.:
|
||||
|
||||
* ``\a``: ASCII code 0x07 (alarm/bell)
|
||||
* ``\b``: ASCII code 0x08 (backspace)
|
||||
* ``\f``: ASCII code 0x0c (formfeed)
|
||||
* ``\n``: ASCII code 0x0a (LF/linefeed/newline)
|
||||
* ``\r``: ASCII code 0x0d (CR/carriage return)
|
||||
* ``\t``: ASCII code 0x09 (tab)
|
||||
* ``\v``: ASCII code 0x0b (vertical tab)
|
||||
* ``\\``: backslash
|
||||
* ``\"``: double quote
|
||||
* ``\123``: Octal 123, etc. for ``\000`` up to ``\377``
|
||||
* ``\x7f``: Hexadecimal 7f, etc. for ``\x00`` up to ``\xff``
|
||||
* `\a`: ASCII code 0x07 (alarm/bell)
|
||||
* `\b`: ASCII code 0x08 (backspace)
|
||||
* `\f`: ASCII code 0x0c (formfeed)
|
||||
* `\n`: ASCII code 0x0a (LF/linefeed/newline)
|
||||
* `\r`: ASCII code 0x0d (CR/carriage return)
|
||||
* `\t`: ASCII code 0x09 (tab)
|
||||
* `\v`: ASCII code 0x0b (vertical tab)
|
||||
* `\\`: backslash
|
||||
* `\"`: double quote
|
||||
* `\123`: Octal 123, etc. for `\000` up to `\377`
|
||||
* `\x7f`: Hexadecimal 7f, etc. for `\x00` up to `\xff`
|
||||
|
||||
See also https://en.wikipedia.org/wiki/Escape_sequences_in_C.
|
||||
See also [https://en.wikipedia.org/wiki/Escape_sequences_in_C](https://en.wikipedia.org/wiki/Escape_sequences_in_C).
|
||||
|
||||
These replacements apply only to strings you key in for the DSL expressions for ``filter`` and ``put``: that is, if you type ``\t`` in a string literal for a ``filter``/``put`` expression, it will be turned into a tab character. If you want a backslash followed by a ``t``, then please type ``\\t``.
|
||||
These replacements apply only to strings you key in for the DSL expressions for `filter` and `put`: that is, if you type `\t` in a string literal for a `filter`/`put` expression, it will be turned into a tab character. If you want a backslash followed by a `t`, then please type `\\t`.
|
||||
|
||||
However, these replacements are not done automatically within your data stream. If you wish to make these replacements, you can do, for example, for a field named ``field``, ``mlr put '$field = gsub($field, "\\t", "\t")'``. If you need to make such a replacement for all fields in your data, you should probably simply use the system ``sed`` command.
|
||||
However, these replacements are not done automatically within your data stream. If you wish to make these replacements, you can do, for example, for a field named `field`, `mlr put '$field = gsub($field, "\\t", "\t")'`. If you need to make such a replacement for all fields in your data, you should probably simply use the system `sed` command.
|
||||
|
||||
|
|
|
|||
|
|
@ -4,41 +4,41 @@ Miller's input and output are all string-oriented: there is (as of August 2015 a
|
|||
|
||||
Field values are treated as numeric for the following:
|
||||
|
||||
* Numeric sort: ``mlr sort -n``, ``mlr sort -nr``.
|
||||
* Statistics: ``mlr histogram``, ``mlr stats1``, ``mlr stats2``.
|
||||
* Cross-record arithmetic: ``mlr step``.
|
||||
* Numeric sort: `mlr sort -n`, `mlr sort -nr`.
|
||||
* Statistics: `mlr histogram`, `mlr stats1`, `mlr stats2`.
|
||||
* Cross-record arithmetic: `mlr step`.
|
||||
|
||||
For ``mlr put`` and ``mlr filter``:
|
||||
For `mlr put` and `mlr filter`:
|
||||
|
||||
* Miller's types for function processing are **empty-null** (empty string), **absent-null** (reads of unset right-hand sides, or fall-through non-explicit return values from user-defined functions), **error**, **string**, **float** (double-precision), **int** (64-bit signed), and **boolean**.
|
||||
|
||||
* On input, string values representable as numbers, e.g. "3" or "3.1", are treated as int or float, respectively. If a record has ``x=1,y=2`` then ``mlr put '$z=$x+$y'`` will produce ``x=1,y=2,z=3``, and ``mlr put '$z=$x.$y'`` does not give an error simply because the dot operator has been generalized to stringify non-strings. To coerce back to string for processing, use the ``string`` function: ``mlr put '$z=string($x).string($y)'`` will produce ``x=1,y=2,z=12``.
|
||||
* On input, string values representable as numbers, e.g. "3" or "3.1", are treated as int or float, respectively. If a record has `x=1,y=2` then `mlr put '$z=$x+$y'` will produce `x=1,y=2,z=3`, and `mlr put '$z=$x.$y'` does not give an error simply because the dot operator has been generalized to stringify non-strings. To coerce back to string for processing, use the `string` function: `mlr put '$z=string($x).string($y)'` will produce `x=1,y=2,z=12`.
|
||||
|
||||
* On input, string values representable as boolean (e.g. ``"true"``, ``"false"``) are *not* automatically treated as boolean. (This is because ``"true"`` and ``"false"`` are ordinary words, and auto string-to-boolean on a column consisting of words would result in some strings mixed with some booleans.) Use the ``boolean`` function to coerce: e.g. giving the record ``x=1,y=2,w=false`` to ``mlr put '$z=($x<$y) || boolean($w)'``.
|
||||
* On input, string values representable as boolean (e.g. `"true"`, `"false"`) are *not* automatically treated as boolean. (This is because `"true"` and `"false"` are ordinary words, and auto string-to-boolean on a column consisting of words would result in some strings mixed with some booleans.) Use the `boolean` function to coerce: e.g. giving the record `x=1,y=2,w=false` to `mlr put '$z=($x<$y) || boolean($w)'`.
|
||||
|
||||
* Functions take types as described in ``mlr --help-all-functions``: for example, ``log10`` takes float input and produces float output, ``gmt2sec`` maps string to int, and ``sec2gmt`` maps int to string.
|
||||
* Functions take types as described in `mlr --help-all-functions`: for example, `log10` takes float input and produces float output, `gmt2sec` maps string to int, and `sec2gmt` maps int to string.
|
||||
|
||||
* All math functions described in ``mlr --help-all-functions`` take integer as well as float input.
|
||||
* All math functions described in `mlr --help-all-functions` take integer as well as float input.
|
||||
|
||||
## String literals
|
||||
|
||||
You can use the following backslash escapes for strings such as between the double quotes in contexts such as ``mlr filter '$name =~ "..."'``, ``mlr put '$name = $othername . "..."'``, ``mlr put '$name = sub($name, "...", "...")``, etc.:
|
||||
You can use the following backslash escapes for strings such as between the double quotes in contexts such as `mlr filter '$name =~ "..."'`, `mlr put '$name = $othername . "..."'`, `mlr put '$name = sub($name, "...", "...")`, etc.:
|
||||
|
||||
* ``\a``: ASCII code 0x07 (alarm/bell)
|
||||
* ``\b``: ASCII code 0x08 (backspace)
|
||||
* ``\f``: ASCII code 0x0c (formfeed)
|
||||
* ``\n``: ASCII code 0x0a (LF/linefeed/newline)
|
||||
* ``\r``: ASCII code 0x0d (CR/carriage return)
|
||||
* ``\t``: ASCII code 0x09 (tab)
|
||||
* ``\v``: ASCII code 0x0b (vertical tab)
|
||||
* ``\\``: backslash
|
||||
* ``\"``: double quote
|
||||
* ``\123``: Octal 123, etc. for ``\000`` up to ``\377``
|
||||
* ``\x7f``: Hexadecimal 7f, etc. for ``\x00`` up to ``\xff``
|
||||
* `\a`: ASCII code 0x07 (alarm/bell)
|
||||
* `\b`: ASCII code 0x08 (backspace)
|
||||
* `\f`: ASCII code 0x0c (formfeed)
|
||||
* `\n`: ASCII code 0x0a (LF/linefeed/newline)
|
||||
* `\r`: ASCII code 0x0d (CR/carriage return)
|
||||
* `\t`: ASCII code 0x09 (tab)
|
||||
* `\v`: ASCII code 0x0b (vertical tab)
|
||||
* `\\`: backslash
|
||||
* `\"`: double quote
|
||||
* `\123`: Octal 123, etc. for `\000` up to `\377`
|
||||
* `\x7f`: Hexadecimal 7f, etc. for `\x00` up to `\xff`
|
||||
|
||||
See also https://en.wikipedia.org/wiki/Escape_sequences_in_C.
|
||||
See also [https://en.wikipedia.org/wiki/Escape_sequences_in_C](https://en.wikipedia.org/wiki/Escape_sequences_in_C).
|
||||
|
||||
These replacements apply only to strings you key in for the DSL expressions for ``filter`` and ``put``: that is, if you type ``\t`` in a string literal for a ``filter``/``put`` expression, it will be turned into a tab character. If you want a backslash followed by a ``t``, then please type ``\\t``.
|
||||
These replacements apply only to strings you key in for the DSL expressions for `filter` and `put`: that is, if you type `\t` in a string literal for a `filter`/`put` expression, it will be turned into a tab character. If you want a backslash followed by a `t`, then please type `\\t`.
|
||||
|
||||
However, these replacements are not done automatically within your data stream. If you wish to make these replacements, you can do, for example, for a field named ``field``, ``mlr put '$field = gsub($field, "\\t", "\t")'``. If you need to make such a replacement for all fields in your data, you should probably simply use the system ``sed`` command.
|
||||
However, these replacements are not done automatically within your data stream. If you wish to make these replacements, you can do, for example, for a field named `field`, `mlr put '$field = gsub($field, "\\t", "\t")'`. If you need to make such a replacement for all fields in your data, you should probably simply use the system `sed` command.
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
The following environment variables affect how Miller works:
|
||||
|
||||
* ``MLRRC``: see [Customization](customization.md)
|
||||
* ``MLR_NO_COLOR``, ``MLR_ALWAYS_COLOR``, ``MLR_KEY_COLOR``, ``MLR_VALUE_COLOR``, ``MLR_PASS_COLOR``, ``MLR_FAIL_COLOR``, ``MLR_REPL_PS1_COLOR``, ``MLR_REPL_PS2_COLOR``, ``MLR_HELP_COLOR``, ``TERM``, * ``MSYSTEM``: see [Output Colorization](output-colorization.md)
|
||||
* ``MLR_REPL_PS1``, ``MLR_REPL_PS2``: see [REPL](repl.md)
|
||||
* `MLRRC`: see [Customization](customization.md).
|
||||
* `MLR_NO_COLOR`, `MLR_ALWAYS_COLOR`, `MLR_KEY_COLOR`, `MLR_VALUE_COLOR`, `MLR_PASS_COLOR`, `MLR_FAIL_COLOR`, `MLR_REPL_PS1_COLOR`, `MLR_REPL_PS2_COLOR`, `MLR_HELP_COLOR`, `TERM`, * `MSYSTEM`: see [Output Colorization](output-colorization.md).
|
||||
* `MLR_REPL_PS1`, `MLR_REPL_PS2`: see [REPL](repl.md).
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue