miller/doc/content-for-faq.html
2017-05-08 18:42:16 -04:00

276 lines
13 KiB
HTML

POKI_PUT_TOC_HERE
<p/>
<button style="font-weight:bold;color:maroon;border:0" onclick="expand_all();" href="javascript:;">Expand all sections</button>
<button style="font-weight:bold;color:maroon;border:0" onclick="collapse_all();" href="javascript:;">Collapse all sections</button>
<h1>No output at all</h1>
<button style="font-weight:bold;color:maroon;border:0" padding=0 onclick="toggle_by_name('section_toggle_no_output_at_all');" href="javascript:;">Toggle section visibility</button>
<div id="section_toggle_no_output_at_all" style="display: block">
<p/>Try <tt>od -xcv</tt> and/or <tt>cat -e</tt> on your file to check for non-printable characters.
<p/>If you&rsquo;re using Miller version less than 5.0.0 (try
<tt>mlr --version</tt> on your system to find out), when the
line-ending-autodetect feature was introduced, please see
<a href="http://johnkerl.org/miller-releases/miller-4.5.0/doc/index.html">here</a>.
</div>
<h1>Fields not selected</h1>
<button style="font-weight:bold;color:maroon;border:0" padding=0 onclick="toggle_by_name('section_toggle_fields_not_selected');" href="javascript:;">Toggle section visibility</button>
<div id="section_toggle_fields_not_selected" style="display: block">
<p/>Check the field-separators of the data, e.g. with the command-line
<tt>head</tt> program. Example: for CSV, Miller&rsquo;s default record
separator is comma; if your data is tab-delimited, e.g. <tt>aTABbTABc</tt>,
then Miller won&rsquo;t find three fields named <tt>a</tt>, <tt>b</tt>, and
<tt>c</tt> but rather just one named <tt>aTABbTABc</tt>. Solution in this
case: <tt>mlr --fs tab {remaining arguments ...}</tt>.
<p/>Also try <tt>od -xcv</tt> and/or <tt>cat -e</tt> on your file to check for non-printable characters.
</div>
<h1>Diagnosing delimiter specifications</h1>
<button style="font-weight:bold;color:maroon;border:0" padding=0 onclick="toggle_by_name('section_toggle_diagnosing_delimiter_specifications');" href="javascript:;">Toggle section visibility</button>
<div id="section_toggle_diagnosing_delimiter_specifications" style="display: block">
POKI_INCLUDE_ESCAPED(data/delimiter-examples.txt)HERE
</div>
<h1>How do I examine then-chaining?</h1>
<button style="font-weight:bold;color:maroon;border:0" padding=0 onclick="toggle_by_name('section_toggle_examine_then_chaining');" href="javascript:;">Toggle section visibility</button>
<div id="section_toggle_examine_then_chaining" style="display: block">
<p/>Then-chaining found in Miller is intended to function the same as Unix
pipes, but with less keystroking. You can print your data one pipeline step at
a time, to see what intermediate output at one step becomes the input to the
next step.
<p/>First, look at the input data:
POKI_RUN_COMMAND{{cat data/then-example.csv}}HERE
Next, run the first step of your command, omitting anything from the first <tt>then</tt> onward:
POKI_RUN_COMMAND{{mlr --icsv --opprint count-distinct -f Status,Payment_Type data/then-example.csv}}HERE
After that, run it with the next <tt>then</tt> step included:
POKI_RUN_COMMAND{{mlr --icsv --opprint count-distinct -f Status,Payment_Type then sort -nr count data/then-example.csv}}HERE
Now if you use <tt>then</tt> to include another verb after that, the columns
<tt>Status</tt>, <tt>Payment_Type</tt>, and <tt>count</tt> will be the input to
that verb.
<p/>Note, by the way, that you&rsquo;ll get the same results using pipes:
POKI_RUN_COMMAND{{mlr --csv count-distinct -f Status,Payment_Type data/then-example.csv | mlr --icsv --opprint sort -nr count}}HERE
</div>
<h1>I assigned $9 and it&rsquo;s not 9th</h1>
<button style="font-weight:bold;color:maroon;border:0" padding=0 onclick="toggle_by_name('section_toggle_9_not_9th');" href="javascript:;">Toggle section visibility</button>
<div id="section_toggle_9_not_9th" style="display: block">
<p/> Miller records are ordered lists of key-value pairs. For NIDX format, DKVP
format when keys are missing, or CSV/CSV-lite format with
<tt>--implicit-csv-header</tt>, Miller will sequentially assign keys of the
form <tt>1</tt>, <tt>2</tt>, etc. But these are not integer array indices:
they&rsquo;re just field names taken from the initial field ordering in the
input data.
POKI_RUN_COMMAND{{echo x,y,z | mlr --dkvp cat}}HERE
POKI_RUN_COMMAND{{echo x,y,z | mlr --dkvp put '$6="a";$4="b";$55="cde"'}}HERE
POKI_RUN_COMMAND{{echo x,y,z | mlr --nidx cat}}HERE
POKI_RUN_COMMAND{{echo x,y,z | mlr --csv --implicit-csv-header cat}}HERE
POKI_RUN_COMMAND{{echo x,y,z | mlr --dkvp rename 2,999}}HERE
POKI_RUN_COMMAND{{echo x,y,z | mlr --dkvp rename 2,newname}}HERE
POKI_RUN_COMMAND{{echo x,y,z | mlr --csv --implicit-csv-header reorder -f 3,1,2}}HERE
</div>
<h1>How can I handle field names with special symbols in them?</h1>
<button style="font-weight:bold;color:maroon;border:0" padding=0 onclick="toggle_by_name('section_toggle_field_names_with_special_symbols');" href="javascript:;">Toggle section visibility</button>
<div id="section_toggle_field_names_with_special_symbols" style="display: block">
<p/>Simply surround the field names with curly braces:
POKI_RUN_COMMAND{{echo 'x.a=3,y:b=4,z/c=5' | mlr put '${product.all} = ${x.a} * ${y:b} * ${z/c}'}}HERE
</div>
<h1>How can I put single-quotes into strings?</h1>
<button style="font-weight:bold;color:maroon;border:0" padding=0 onclick="toggle_by_name('section_toggle_single_quotes_in_strings');" href="javascript:;">Toggle section visibility</button>
<div id="section_toggle_single_quotes_in_strings" style="display: block">
<p/> This is a little tricky due to the shell&rsquo;s handling of quotes. For simplicity, let&rsquo;s first put
an update script into a file:
POKI_INCLUDE_ESCAPED(data/single-quote-example.mlr)HERE
POKI_RUN_COMMAND{{echo a=bcd | mlr put -f data/single-quote-example.mlr}}HERE
<p/>So, it&rsquo;s simple: Miller&rsquo;s DSL uses double quotes for strings,
and you can put single quotes (or backslash-escaped double-quotes) inside
strings, no problem.
<p/> Without putting the update expression in a file, it&rsquo;s messier:
POKI_RUN_COMMAND{{echo a=bcd | mlr put '$a="It'\''s OK, I said, '\''for now'\''."'}}HERE
<p/> The idea is that the outermost single-quotes are to protect the
<tt>put</tt> expression from the shell, and the double quotes within them are
for Miller. To get a single quote in the middle there, you need to actually put it <i>outside</i> the single-quoting
for the shell. The pieces are
<ul>
<li/> <tt>$a="It</tt>
<li/> <tt>\'</tt>
<li/> <tt>s OK, I said,</tt>
<li/> <tt>\'</tt>
<li/> <tt>for now</tt>
<li/> <tt>\'</tt>
<li/> <tt>.</tt>
</ul>
all concatenated together.
</div>
<h1>Why doesn&rsquo;t mlr cut put fields in the order I want?</h1>
<button style="font-weight:bold;color:maroon;border:0" padding=0 onclick="toggle_by_name('section_toggle_cut_out_of_order');" href="javascript:;">Toggle section visibility</button>
<div id="section_toggle_cut_out_of_order" style="display: block">
<p/>Example: columns <tt>x,i,a</tt> were requested but they appear here in the order <tt>a,i,x</tt>:
POKI_RUN_COMMAND{{cat data/small}}HERE
POKI_RUN_COMMAND{{mlr cut -f x,i,a data/small}}HERE
<p/>The issue is that Miller&rsquo;s <tt>cut</tt>, by default, outputs cut fields in the order they
appear in the input data. This design decision was made intentionally to parallel the *nix system <tt>cut</tt>
command, which has the same semantics.
<p/>The solution is to use the <tt>-o</tt> option:
POKI_RUN_COMMAND{{mlr cut -o -f x,i,a data/small}}HERE
</div>
<h1>NR is not consecutive after then-chaining</h1>
<button style="font-weight:bold;color:maroon;border:0" padding=0 onclick="toggle_by_name('section_toggle_NR_not_consecutive_after_then');" href="javascript:;">Toggle section visibility</button>
<div id="section_toggle_NR_not_consecutive_after_then" style="display: block">
<p/> Given this input data:
POKI_RUN_COMMAND{{cat data/small}}HERE
why don&rsquo;t I see <tt>NR=1</tt> and <tt>NR=2</tt> here??
POKI_RUN_COMMAND{{mlr filter '$x > 0.5' then put '$NR = NR' data/small}}HERE
<p/>The reason is that <tt>NR</tt> is computed for the original input records and isn&rsquo;t dynamically
updated. By contrast, <tt>NF</tt> is dynamically updated: it&rsquo;s the number of fields in the
current record, and if you add/remove a field, the value of <tt>NF</tt> will change:
POKI_RUN_COMMAND{{echo x=1,y=2,z=3 | mlr put '$nf1 = NF; $u = 4; $nf2 = NF; unset $x,$y,$z; $nf3 = NF'}}HERE
<p/><tt>NR</tt>, by contrast (and <tt>FNR</tt> as well), retains the value from the original input stream,
and records may be dropped by a <tt>filter</tt> within a <tt>then</tt>-chain. To recover consecutive record
numbers, you can use out-of-stream variables as follows:
POKI_INCLUDE_AND_RUN_ESCAPED(data/dynamic-nr.sh)HERE
<p/>Or, simply use <tt>mlr cat -n</tt>:
POKI_RUN_COMMAND{{mlr filter '$x > 0.5' then cat -n data/small}}HERE
</div>
<h1>Why am I not seeing all possible joins occur?</h1>
<button style="font-weight:bold;color:maroon;border:0" padding=0 onclick="toggle_by_name('section_toggle_not_all_possible_joins');" href="javascript:;">Toggle section visibility</button>
<div id="section_toggle_not_all_possible_joins" style="display: block">
<p/><b>This section describes behavior before Miller 5.1.0. As of 5.1.0, <tt>-u</tt> is the default.</b>
<p/>For example, the right file here has nine records, and the left file should
add in the <tt>hostname</tt> column &mdash; so the join output should also have
9 records:
POKI_RUN_COMMAND{{mlr --icsvlite --opprint cat data/join-u-left.csv}}HERE
POKI_RUN_COMMAND{{mlr --icsvlite --opprint cat data/join-u-right.csv}}HERE
POKI_RUN_COMMAND{{mlr --icsvlite --opprint join -s -j ipaddr -f data/join-u-left.csv data/join-u-right.csv}}HERE
<p/>The issue is that Miller&rsquo;s <tt>join</tt>, 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 *nix
system <tt>join</tt> 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.
<p/>The solution (besides pre-sorting the input files on the join keys) is to
simply use <b>mlr join -u</b> (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:
POKI_RUN_COMMAND{{mlr --icsvlite --opprint join -u -j ipaddr -f data/join-u-left.csv data/join-u-right.csv}}HERE
<p/>General advice is to make sure the left-file is relatively small, e.g.
containing name-to-number mappings, while saving large amounts of data for the
right file.
</div>
<h1>What about XML or JSON file formats?</h1>
<button style="font-weight:bold;color:maroon;border:0" padding=0 onclick="toggle_by_name('section_toggle_xml_or_json');" href="javascript:;">Toggle section visibility</button>
<div id="section_toggle_xml_or_json" style="display: block">
<p/>Miller handles <boldmaroon>tabular data</boldmaroon>, which is a list of
records each having fields which are key-value pairs. Miller also doesn&rsquo;t
require that each record have the same field names (see also <a
href="record-heterogeneity.html">here</a>). Regardless, tabular data is a
<boldmaroon>non-recursive data structure</boldmaroon>.
<p/> XML, JSON, etc. are, by contrast, all <boldmaroon>recursive</boldmaroon>
or <boldmaroon>nested</boldmaroon> data structures. For example, in JSON
you can represent a hash map whose values are lists of lists.
<p/>Now, you can put tabular data into these formats &mdash; since list-of-key-value-pairs
is one of the things representable in XML or JSON. Example:
<p/>
<div class="pokipanel">
<pre>
# DKVP
x=1,y=2
z=3
# XML
&lt;table&gt;
&lt;record&gt;
&lt;field&gt;
&lt;key&gt; x &lt;/key&gt; &lt;value&gt; 1 &lt;/value&gt;
&lt;/field&gt;
&lt;field&gt;
&lt;key&gt; y &lt;/key&gt; &lt;value&gt; 2 &lt;/value&gt;
&lt;/field&gt;
&lt;/record&gt;
&lt;field&gt;
&lt;key&gt; z &lt;/key&gt; &lt;value&gt; 3 &lt;/value&gt;
&lt;/field&gt;
&lt;record&gt;
&lt;/record&gt;
&lt;/table&gt;
# JSON
[{"x":1,"y":2},{"z":3}]
</pre>
</div>
<p/>However, a tool like Miller which handles non-recursive data is never going
to be able to handle full XML/JSON semantics &mdash; only a small subset. If
tabular data represented in XML/JSON/etc are sufficiently well-structured, it
may be easy to grep/sed out the data into a simpler text form &mdash; this is a
general text-processing problem.
<p/>Miller does support tabular data represented in JSON: please see
POKI_PUT_LINK_FOR_PAGE(file-formats.html)HERE. See also <a
href="http://stedolan.github.io/jq/">jq</a> for a truly powerful, JSON-specific
tool.
<p/>For XML, my suggestion is to use a tool like
<a href="http://ff-extractor.sourceforge.net/">ff-extractor</a> to do format
conversion.
</div>