I think that this is a really good idea in that<div><ol><li>it is consistent with the current implementation</li><li>it is quite flexible.</li></ol><div>One only has to do decide what to do when the the user gives invalid integer, i.e. integers larger .N. Either one throws an error or gives a warning and take .N. What do the others think of Matthew&#39;s proposal?</div>

<div><br></div><div>On a side note: </div><div><br></div><div>I just played around with the mult= option and discovered the following:</div><div><br></div><div><div>DT &lt;- data.table(X = rnorm(100),</div><div>                 Y = rnorm(100),</div>

<div>                 C =rep(c(1, 2), each=100),</div><div>                 key=&quot;C&quot;)</div></div><div>all.equal(DT[C==1, mult=&quot;first&quot;], DT[C==1, mult=&quot;all&quot;])</div><div>all.equal(DT[1:100, mult=&quot;first&quot;], DT[1:100, mult=&quot;all&quot;])</div>

<div>#<span style="font-size:10pt;line-height:1.3;white-space:pre-wrap;font-family:monospace;text-align:-webkit-left">[1] TRUE</span></div><div><span style="font-size:10pt;line-height:1.3;white-space:pre-wrap;font-family:monospace;text-align:-webkit-left"><br>

</span></div><div><span style="font-size:10pt;line-height:1.3;white-space:pre-wrap;font-family:monospace;text-align:-webkit-left">For me, it is rather surprising that both mult=&quot;all&quot; and mult=&quot;first&quot; return all rows that match C==1, i.e. in the case when i is a logical or integer. From the help page: &quot;</span><span style="font-family:sans-serif;font-size:13px">When</span><span style="font-family:sans-serif;font-size:13px"> </span><em style="font-family:sans-serif;font-size:13px">multiple</em><span style="font-family:sans-serif;font-size:13px"> </span><span style="font-family:sans-serif;font-size:13px">rows in</span><span style="font-family:sans-serif;font-size:13px"> </span><code style="font-size:13px">x</code><span style="font-family:sans-serif;font-size:13px"> </span><span style="font-family:sans-serif;font-size:13px">match to the row in</span><span style="font-family:sans-serif;font-size:13px"> </span><code style="font-size:13px">i</code><span style="font-family:sans-serif;font-size:13px">,</span><span style="font-family:sans-serif;font-size:13px"> </span><code style="font-size:13px">mult</code><span style="font-family:sans-serif;font-size:13px"> </span><span style="font-family:sans-serif;font-size:13px">controls which are returned:</span><span style="font-family:sans-serif;font-size:13px"> </span><code style="font-size:13px">&quot;all&quot;</code><span style="font-family:sans-serif;font-size:13px"> </span><span style="font-family:sans-serif;font-size:13px">(default),</span><span style="font-family:sans-serif;font-size:13px"> </span><code style="font-size:13px">&quot;first&quot;</code><span style="font-family:sans-serif;font-size:13px"> </span><span style="font-family:sans-serif;font-size:13px">or</span><span style="font-family:sans-serif;font-size:13px"> </span><code style="font-size:13px">&quot;last&quot;</code><span style="font-family:sans-serif;font-size:13px">.&quot; Although one could argue that a &quot;row&quot; only applies to a data.table and not a vector, as a user I would expect mult= to work on both, i.e. when i is a data.table, a logical or an integer. However, it only works for data.tables, which I checked in the source code. So should I update the documentation to make it clearer that mult= only works if i is a data.table?</span></div>

<br><div class="gmail_quote">On Sun, Jan 8, 2012 at 8:25 PM, Matthew Dowle <span dir="ltr">&lt;<a href="mailto:mdowle@mdowle.plus.com">mdowle@mdowle.plus.com</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">

How about allowing mult to be integer (or an expression that evaluates<br>
to integer) :<br>
<br>
DT[X, mult=&quot;first&quot;]<br>
DT[X, mult=1L]   # same<br>
<br>
DT[X, mult=&quot;last&quot;]<br>
DT[X, mult=.N]   # same<br>
<br>
DT[X, .SD[2]]  # 2nd row of each group (inefficient due to .SD, there<br>
are other longer alternatives)<br>
DT[X, mult=2L] # same, but efficient and simple<br>
<br>
DT[X, mult=&quot;random&quot;]<br>
DT[X, mult=sample(.N,size=1)]  # same, but more general<br>
<br>
DT[X, mult=-1L]   # all but the first of each group<br>
<span class="HOEnZb"><font color="#888888"><br>
Matthew<br>
</font></span><div class="HOEnZb"><div class="h5"><br>
On Sat, 2012-01-07 at 19:07 -0800, Steven C. Bagley wrote:<br>
&gt; The mult argument is becoming its own little programming language. I<br>
&gt; worry that this is going to get complicated in an ad hoc way. What if<br>
&gt; someone wants random, but with weighting? Each new value of mult is<br>
&gt; really shorthand for an R language construct. Maybe there is a more<br>
&gt; general way to express these ideas using existing R constructs? (I&#39;m<br>
&gt; not sure how to do this consistently. I&#39;m merely making an<br>
&gt; observation.)<br>
&gt;<br>
&gt;<br>
&gt; --Steve<br>
&gt;<br>
&gt;<br>
&gt; On Jan 6, 2012, at 5:58 AM, Christoph Jäckel wrote:<br>
&gt;<br>
&gt; &gt; Thanks for your feedback. @Chris: I guess Matthew&#39;s example and<br>
&gt; &gt; your&#39;s do not really match because he doesn&#39;t call sample(dt,...),<br>
&gt; &gt; but sample(dt[i, which=TRUE],... His option, though, returns all the<br>
&gt; &gt; rows that match between the keys of dt and i and takes a random<br>
&gt; &gt; sample of size 1 from that, so I guess it does what I expected.<br>
&gt; &gt; Nevertheless, I think an option mult=&quot;random&quot; would still be useful.<br>
&gt; &gt; Here is why:<br>
&gt; &gt;<br>
&gt; &gt;<br>
&gt; &gt; I guess my first example was a little bit too simplistic, sorry for<br>
&gt; &gt; that! Here is an updated, more realistic example of what I do and<br>
&gt; &gt; some hints about my current implementation of mult=&quot;random&quot;:<br>
&gt; &gt;<br>
&gt; &gt;<br>
&gt; &gt; require(data.table)<br>
&gt; &gt; rawData &lt;- data.table(fundID = 1:1e5,<br>
&gt; &gt;                       Year   = rep(1:10, times=1e4),<br>
&gt; &gt;                       key    = &quot;Year&quot;)<br>
&gt; &gt; #Let&#39;s have 10000 runs; in each run we want to draw a fund with a<br>
&gt; &gt; year that is<br>
&gt; &gt; #set dynamically<br>
&gt; &gt; intJoin &lt;- J(sample(1:10, size=10000, replace=TRUE))<br>
&gt; &gt;<br>
&gt; &gt;<br>
&gt; &gt; #Best solution I have come up so far with the current options in<br>
&gt; &gt; data.table<br>
&gt; &gt; #Is there one that can beat mult=&quot;random&quot; and is easy for the user<br>
&gt; &gt; to implement?<br>
&gt; &gt; foo1 &lt;- function(n, intJoin, rawData) {<br>
&gt; &gt;     x &lt;- integer(n)<br>
&gt; &gt;     for (r in seq_len(nrow(intJoin))) {<br>
&gt; &gt;       x[r] &lt;- sample(rawData[intJoin[r], which=TRUE], size=1)<br>
&gt; &gt;     }<br>
&gt; &gt;     return(rawData[x])<br>
&gt; &gt; }<br>
&gt; &gt; system.time(finalData &lt;- foo1(10000, intJoin, rawData))<br>
&gt; &gt; #    user  system elapsed<br>
&gt; &gt; #  43.827   0.000  44.232<br>
&gt; &gt; #Check that it does what it should: match random entities to the<br>
&gt; &gt; exact year in intJoin<br>
&gt; &gt; cbind(finalData, intJoin)<br>
&gt; &gt; #       fundID Year V1<br>
&gt; &gt; #  [1,]  46556    6  6<br>
&gt; &gt; #  [2,]  77642    2  2<br>
&gt; &gt; #  [3,]  17325    5  5<br>
&gt; &gt; #  [4,]  36617    7  7<br>
&gt; &gt; #  [5,]  90697    7  7<br>
&gt; &gt; #  [6,]   4536    6  6<br>
&gt; &gt; #  [7,]  22273    3  3<br>
&gt; &gt; #  [8,]  46825    5  5<br>
&gt; &gt; #  [9,]  65788    8  8<br>
&gt; &gt; # [10,]  14153    3  3<br>
&gt; &gt;<br>
&gt; &gt;<br>
&gt; &gt; #My implementation of mult=&quot;random&quot;<br>
&gt; &gt; system.time(finalData &lt;- rawData[intJoin, mult=&quot;random&quot;])<br>
&gt; &gt; #   user  system elapsed<br>
&gt; &gt; #  0.324   0.016   0.337<br>
&gt; &gt; #Pretty fast and easy to understand<br>
&gt; &gt; #Check that it does what it should: match random entities to the<br>
&gt; &gt; exact year in intJoin<br>
&gt; &gt; cbind(finalData, intJoin)<br>
&gt; &gt; #       Year fundID V1<br>
&gt; &gt; #  [1,]    6  39626  6<br>
&gt; &gt; #  [2,]    2  98552  2<br>
&gt; &gt; #  [3,]    5  85425  5<br>
&gt; &gt; #  [4,]    7  24637  7<br>
&gt; &gt; #  [5,]    7  74797  7<br>
&gt; &gt; #  [6,]    6  87626  6<br>
&gt; &gt; #  [7,]    3  88973  3<br>
&gt; &gt; #  [8,]    5  60335  5<br>
&gt; &gt; #  [9,]    8  62298  8<br>
&gt; &gt; # [10,]    3  23283  3<br>
&gt; &gt;<br>
&gt; &gt;<br>
&gt; &gt; If you want to try it out yourself: Just call<br>
&gt; &gt;<br>
&gt; &gt;<br>
&gt; &gt; fixInNamespace(&quot;[.data.table&quot;, pos=&quot;package:data.table&quot;)<br>
&gt; &gt;<br>
&gt; &gt;<br>
&gt; &gt; and change the following lines in the editor (this applies to<br>
&gt; &gt; data.table 1.7.7):<br>
&gt; &gt;<br>
&gt; &gt; OLD LINE:     if (!mult %in% c(&quot;first&quot;, &quot;last&quot;, &quot;all&quot;)) stop(&quot;mult<br>
&gt; &gt; argument can only be &#39;first&#39;,&#39;last&#39; or &#39;all&#39;&quot;)<br>
&gt; &gt; NEW LINE:     if (!mult %in% c(&quot;first&quot;,&quot;last&quot;,&quot;all&quot;, &quot;random&quot;))<br>
&gt; &gt; stop(&quot;mult argument can only be &#39;first&#39;,&#39;last&#39;, &#39;all&#39;, or &#39;random&#39;&quot;)<br>
&gt; &gt;<br>
&gt; &gt;<br>
&gt; &gt; and<br>
&gt; &gt;<br>
&gt; &gt;<br>
&gt; &gt; OLD LINES: else {<br>
&gt; &gt;                 irows = if (mult == &quot;first&quot;)<br>
&gt; &gt;                   idx.start<br>
&gt; &gt;                 else idx.end<br>
&gt; &gt;                 lengths = rep(1L, length(irows))<br>
&gt; &gt;             }<br>
&gt; &gt;<br>
&gt; &gt;<br>
&gt; &gt; NEW LINES:  } else if (mult==&quot;first&quot;) {<br>
&gt; &gt;               irows = idx.start<br>
&gt; &gt;               lengths=rep(1L,length(irows))<br>
&gt; &gt;             } else if (mult==&quot;last&quot;) {<br>
&gt; &gt;               irows = idx.end<br>
&gt; &gt;               lengths=rep(1L,length(irows))<br>
&gt; &gt;             } else {<br>
&gt; &gt;               irows = mapply(function(x1, x2) {sample(x1:x2,<br>
&gt; &gt; size=1)}, idx.start, idx.end)<br>
&gt; &gt;               lengths = rep(1L,length(irows))<br>
&gt; &gt;             }<br>
&gt; &gt;<br>
&gt; &gt; However, I don&#39;t know what&#39;s going on in the line<br>
&gt; &gt; .Call(&quot;binarysearch&quot;, i, x, as.integer(leftcols -<br>
&gt; &gt;                 1), as.integer(rightcols - 1), haskey(i), roll,<br>
&gt; &gt;                 rolltolast, idx.start, idx.end, PACKAGE =<br>
&gt; &gt; &quot;data.table&quot;)<br>
&gt; &gt;<br>
&gt; &gt;<br>
&gt; &gt; I figured out that idx.start and idx.end are changed with this<br>
&gt; &gt; function call and I guess at this point in the function it should<br>
&gt; &gt; always be that idx.start and idx.end are of the same lenght and both<br>
&gt; &gt; return only integer values that represent rows of x, but here I&#39;m<br>
&gt; &gt; not 100% sure. So maybe additional checks are needed in the else<br>
&gt; &gt; clause when the mapply-function is called.<br>
&gt; &gt;<br>
&gt; &gt;<br>
&gt; &gt; So let me know what you think. I will join the project independent<br>
&gt; &gt; of that particular issue and try to help, but I guess I should start<br>
&gt; &gt; with simple things. So if there is any help needed on documentation<br>
&gt; &gt; checking<br>
&gt; &gt; or stuff like that, just let me know and I try my best!<br>
&gt; &gt;<br>
&gt; &gt; Christoph<br>
&gt; &gt;<br>
&gt; &gt; On Fri, Jan 6, 2012 at 1:52 PM, Chris Neff &lt;<a href="mailto:caneff@gmail.com">caneff@gmail.com</a>&gt; wrote:<br>
&gt; &gt;         That isn&#39;t doing quite what he does.  I don&#39;t know what you<br>
&gt; &gt;         expected<br>
&gt; &gt;<br>
&gt; &gt;         sample(dt, size=1)<br>
&gt; &gt;<br>
&gt; &gt;         to do but it seems to essentially do this:<br>
&gt; &gt;<br>
&gt; &gt;         dt[sample(1:ncol(dt),size=1),]<br>
&gt; &gt;<br>
&gt; &gt;         It picking a random column number and then return that row<br>
&gt; &gt;         instead.<br>
&gt; &gt;         Try it for yourself:<br>
&gt; &gt;<br>
&gt; &gt;         dt=data.table(x=1:10,y=1:10,z=1:10)<br>
&gt; &gt;         sample(dt, size=1)<br>
&gt; &gt;<br>
&gt; &gt;         The only rows you will get is 1,1,1 2,2,2 and 3,3,3.  Caveat<br>
&gt; &gt;         as usual<br>
&gt; &gt;         is I&#39;m on 1.7.1 until my crashing bug is fixed so apologies<br>
&gt; &gt;         if this<br>
&gt; &gt;         works properly in later versions.<br>
&gt; &gt;<br>
&gt; &gt;         Note that this diverges from what sample(df, size=1) does,<br>
&gt; &gt;         which is<br>
&gt; &gt;         picks a random column and returns that whole column.<br>
&gt; &gt;<br>
&gt; &gt;         What he really wants is to pick a random row from each<br>
&gt; &gt;         subset (I<br>
&gt; &gt;         think). None of your examples do that and I can&#39;t think of a<br>
&gt; &gt;         simpler<br>
&gt; &gt;         way than what he suggests.<br>
&gt; &gt;<br>
&gt; &gt;         On 6 January 2012 03:34, Matthew Dowle<br>
&gt; &gt;         &lt;<a href="mailto:mdowle@mdowle.plus.com">mdowle@mdowle.plus.com</a>&gt; wrote:<br>
&gt; &gt;         &gt; Very keen for direct contributions in that way, happy to<br>
&gt; &gt;         help you with<br>
&gt; &gt;         &gt; svn etc, and you joining the project.<br>
&gt; &gt;         &gt;<br>
&gt; &gt;         &gt; In this particular example, how about :<br>
&gt; &gt;         &gt;<br>
&gt; &gt;         &gt;    rawData[sample(rawData[J(&quot;eu&quot;), which=TRUE],size=1)]<br>
&gt; &gt;         &gt;<br>
&gt; &gt;         &gt; This solves the inefficiency of the 1st step; i.e.,<br>
&gt; &gt;         &gt;    intDT &lt;- rawData[J(&quot;eu&quot;), mult=&quot;all&quot;]<br>
&gt; &gt;         &gt; which copies a subset of all the columns, whilst retaining<br>
&gt; &gt;         flexibility<br>
&gt; &gt;         &gt; for the user so user can easily sample 2 rows, or any<br>
&gt; &gt;         other R method to<br>
&gt; &gt;         &gt; select a random subset.<br>
&gt; &gt;         &gt;<br>
&gt; &gt;         &gt; Because of potential scoping conflicts (say a column was<br>
&gt; &gt;         called<br>
&gt; &gt;         &gt; &quot;rawData&quot; i.e. the same name of the table), to be more<br>
&gt; &gt;         robust :<br>
&gt; &gt;         &gt;<br>
&gt; &gt;         &gt; x = sample(rawData[J(&quot;eu&quot;), which=TRUE],size=1)<br>
&gt; &gt;         &gt; rawData[x]<br>
&gt; &gt;         &gt;<br>
&gt; &gt;         &gt; This is slightly different because when i is a single name<br>
&gt; &gt;         (x in this<br>
&gt; &gt;         &gt; case), data.table knows the caller must mean the x in<br>
&gt; &gt;         calling scope, not<br>
&gt; &gt;         &gt; the column called &quot;x&quot; (if any).  Is two steps like this<br>
&gt; &gt;         ok?  I&#39;m<br>
&gt; &gt;         &gt; guessing it was really the inefficiency that was the<br>
&gt; &gt;         motivation?<br>
&gt; &gt;         &gt;<br>
&gt; &gt;         &gt; Matthew<br>
&gt; &gt;         &gt;<br>
&gt; &gt;         &gt; On Fri, 2012-01-06 at 00:20 +0100, Christoph Jäckel wrote:<br>
&gt; &gt;         &gt;&gt; Hi together,<br>
&gt; &gt;         &gt;&gt;<br>
&gt; &gt;         &gt;&gt;<br>
&gt; &gt;         &gt;&gt; I run a Monte Carlo simulation on a data.table and do<br>
&gt; &gt;         that currently<br>
&gt; &gt;         &gt;&gt; with a loop: on every run, I choose a subset of rows<br>
&gt; &gt;         subject to<br>
&gt; &gt;         &gt;&gt; certain criteria and from those rows I take a random<br>
&gt; &gt;         element.<br>
&gt; &gt;         &gt;&gt; Currently, I do the following: Let&#39;s say I have funds<br>
&gt; &gt;         from two regions<br>
&gt; &gt;         &gt;&gt; (&quot;eu&quot; and &quot;us&quot;) and I want to choose a random fund from<br>
&gt; &gt;         &quot;eu&quot; (could be<br>
&gt; &gt;         &gt;&gt; &quot;us&quot; in the next run and a different region in the<br>
&gt; &gt;         third):<br>
&gt; &gt;         &gt;&gt;<br>
&gt; &gt;         &gt;&gt;<br>
&gt; &gt;         &gt;&gt; library(data.table)<br>
&gt; &gt;         &gt;&gt; rawData &lt;- data.table(fundID  = letters,<br>
&gt; &gt;         &gt;&gt;                       compGeo = rep(c(&quot;us&quot;, &quot;eu&quot;),<br>
&gt; &gt;         each=13))<br>
&gt; &gt;         &gt;&gt; setkey(rawData, &quot;compGeo&quot;)<br>
&gt; &gt;         &gt;&gt; intDT &lt;- rawData[J(&quot;eu&quot;), mult=&quot;all&quot;]<br>
&gt; &gt;         &gt;&gt; intDT[<a href="http://sample.int" target="_blank">sample.int</a>(nrow(intDT), size=1)]<br>
&gt; &gt;         &gt;&gt;<br>
&gt; &gt;         &gt;&gt;<br>
&gt; &gt;         &gt;&gt; So my idea is to just give the user the option<br>
&gt; &gt;         mult=&quot;random&quot;, which<br>
&gt; &gt;         &gt;&gt; does this in one step. What do you think about that<br>
&gt; &gt;         feature request?<br>
&gt; &gt;         &gt;&gt;<br>
&gt; &gt;         &gt;&gt;<br>
&gt; &gt;         &gt;&gt; With respect to the implementation: I changed a few lines<br>
&gt; &gt;         in the<br>
&gt; &gt;         &gt;&gt; function &#39;[.data.table&#39; and got this to run on my locale<br>
&gt; &gt;         data.table<br>
&gt; &gt;         &gt;&gt; version, so I guess I could implement it (as far as I can<br>
&gt; &gt;         see, one<br>
&gt; &gt;         &gt;&gt; just needs to change some R code). However, I haven&#39;t<br>
&gt; &gt;         done extensive<br>
&gt; &gt;         &gt;&gt; testing and I&#39;m not an expert on shared projects and<br>
&gt; &gt;         subversion (never<br>
&gt; &gt;         &gt;&gt; did that actually), so I guess I would need some help to<br>
&gt; &gt;         start with<br>
&gt; &gt;         &gt;&gt; and the confirmation I couldn&#39;t break anything ;-)<br>
&gt; &gt;         &gt;&gt;<br>
&gt; &gt;         &gt;&gt;<br>
&gt; &gt;         &gt;&gt; Christoph<br>
&gt; &gt;         &gt;&gt;<br>
&gt; &gt;         &gt;&gt;<br>
&gt; &gt;         &gt;&gt;<br>
&gt; &gt;         &gt;&gt;<br>
&gt; &gt;         &gt;&gt; _______________________________________________<br>
&gt; &gt;         &gt;&gt; datatable-help mailing list<br>
&gt; &gt;         &gt;&gt; <a href="mailto:datatable-help@lists.r-forge.r-project.org">datatable-help@lists.r-forge.r-project.org</a><br>
&gt; &gt;         &gt;&gt;<br>
&gt; &gt;         <a href="https://lists.r-forge.r-project.org/cgi-bin/mailman/listinfo/datatable-help" target="_blank">https://lists.r-forge.r-project.org/cgi-bin/mailman/listinfo/datatable-help</a><br>
&gt; &gt;         &gt;<br>
&gt; &gt;         &gt;<br>
&gt; &gt;         &gt; _______________________________________________<br>
&gt; &gt;         &gt; datatable-help mailing list<br>
&gt; &gt;         &gt; <a href="mailto:datatable-help@lists.r-forge.r-project.org">datatable-help@lists.r-forge.r-project.org</a><br>
&gt; &gt;         &gt;<br>
&gt; &gt;         <a href="https://lists.r-forge.r-project.org/cgi-bin/mailman/listinfo/datatable-help" target="_blank">https://lists.r-forge.r-project.org/cgi-bin/mailman/listinfo/datatable-help</a><br>
&gt; &gt;<br>
&gt; &gt;<br>
&gt; &gt;<br>
&gt; &gt;<br>
&gt; &gt; _______________________________________________<br>
&gt; &gt; datatable-help mailing list<br>
&gt; &gt; <a href="mailto:datatable-help@lists.r-forge.r-project.org">datatable-help@lists.r-forge.r-project.org</a><br>
&gt; &gt; <a href="https://lists.r-forge.r-project.org/cgi-bin/mailman/listinfo/datatable-help" target="_blank">https://lists.r-forge.r-project.org/cgi-bin/mailman/listinfo/datatable-help</a><br>
&gt;<br>
&gt; _______________________________________________<br>
&gt; datatable-help mailing list<br>
&gt; <a href="mailto:datatable-help@lists.r-forge.r-project.org">datatable-help@lists.r-forge.r-project.org</a><br>
&gt; <a href="https://lists.r-forge.r-project.org/cgi-bin/mailman/listinfo/datatable-help" target="_blank">https://lists.r-forge.r-project.org/cgi-bin/mailman/listinfo/datatable-help</a><br>
<br>
<br>
</div></div></blockquote></div><br><br clear="all"><div><br></div><br>
</div>