Count number occurrence of value per row

username: enemis
cooked: <p>Hi,<br>
I havent found exactly this problem before, and keen if anyone has any ideas. I have a 2d matrix <code>x</code> containing integer values. I want to count how many of each value per row and return a new 2d matrix <code>num_of_value</code>  that has the same number of rows, but the columns represent different values. So in num_of_value[0,1] is the number of occurences of <code>1</code> in row=0 and so on. My best solution so far is a loop, which might be inefficient for large number of unique values.</p>
<p>Any ideas?</p>
<pre><code class="lang-auto">#%%
x =  torch.tensor([[1, 2, 3, 4, 2, 5, 6, 7, 8, 9, 7, 0, 0, 6, 9, 0, 1, 0, 0],
        [1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, 1, 4, 1, 1, 2, 3, 1]])
# %%
# Loop through all values:

num_of_value = torch.zeros((len(x), x.max()+1), device=self.device)
for k in range(0,max_cluster_idx):
    num_k = (x == k).sum(dim=1)
    num_of_value[:,k] = num_k

print(num_of_value)
tensor([[ 5.,  2.,  2.,  1.,  1.,  1.,  2.,  2.,  1.,  2.],
        [ 0., 13.,  2.,  3.,  1.,  0.,  0.,  0.,  0.,  0.]])
</code></pre>

username: KFrank
cooked: <p>Hi Simen!</p>
<aside class="quote no-group" data-username="enemis" data-post="1" data-topic="137061" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img loading="lazy" alt="" width="24" height="24" src="https://discuss.pytorch.org/user_avatar/discuss.pytorch.org/enemis/48/9277_2.png" class="avatar"> enemis:</div>
<blockquote>
<pre><code class="lang-auto">tensor([[ 5.,  2.,  2.,  1.,  1.,  1.,  2.,  2.,  1.,  2.],
        [ 0., 13.,  2.,  3.,  1.,  0.,  0.,  0.,  0.,  0.]])
</code></pre>
</blockquote>
</aside>
<p>Please try <code>torch.nn.functional.one_hot (x).sum (dim = 1)</code>.</p>
<p>Best.</p>
<p>K. Frank</p>

username: enemis
cooked: <aside class="quote no-group" data-username="KFrank" data-post="2" data-topic="137061">
<div class="title">
<div class="quote-controls"></div>
<img loading="lazy" alt="" width="24" height="24" src="https://discuss.pytorch.org/letter_avatar_proxy/v4/letter/k/ecb155/48.png" class="avatar"> KFrank:</div>
<blockquote>
<p>torch.nn.functional.one_hot (x).sum (dim = 1)</p>
</blockquote>
</aside>
<p>Wow, yes, of course! thank you so much!</p>

username: lyprince
cooked: <p>Is there a way to do this without materializing a one-hot matrix? - This is very memory-inefficient for large k</p>

username: KFrank
cooked: <p>Hi Lyprince!</p>
<aside class="quote no-group" data-username="lyprince" data-post="4" data-topic="137061" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img loading="lazy" alt="" width="24" height="24" src="https://discuss.pytorch.org/user_avatar/discuss.pytorch.org/lyprince/48/63887_2.png" class="avatar"> lyprince:</div>
<blockquote>
<p>Is there a way to do this without materializing a one-hot matrix? - This is very memory-inefficient for large k</p>
</blockquote>
</aside>
<p>Yes.  You can use <code>torch.bincount()</code> if you add a row-label “offset” value to your<br>
input tensor and then flatten it.</p>
<p>Consider:</p>
<pre><code class="lang-plaintext">&gt;&gt;&gt; import torch
&gt;&gt;&gt; print (torch.__version__)
2.0.1
&gt;&gt;&gt;
&gt;&gt;&gt; x = torch.tensor([[1, 2, 3, 4, 2, 5, 6, 7, 8, 9, 7, 0, 0, 6, 9, 0, 1, 0, 0],
...                    [1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, 1, 4, 1, 1, 2, 3, 1]])
&gt;&gt;&gt;
&gt;&gt;&gt; r = torch.tensor([[ 5.,  2.,  2.,  1.,  1.,  1.,  2.,  2.,  1.,  2.],
...                   [ 0., 13.,  2.,  3.,  1.,  0.,  0.,  0.,  0.,  0.]])
&gt;&gt;&gt;
&gt;&gt;&gt; xlim = x.max() + 1
&gt;&gt;&gt; xrow = x.size (0)
&gt;&gt;&gt; minl = xlim * xrow
&gt;&gt;&gt;
&gt;&gt;&gt; xlab = x + xlim * torch.arange (xrow).unsqueeze (1)
&gt;&gt;&gt; xtmp = torch.bincount (xlab.flatten(), minlength = minl)
&gt;&gt;&gt; xcnt = xtmp.reshape (xrow, xlim)
&gt;&gt;&gt;
&gt;&gt;&gt; torch.equal (xcnt, r.long())
True
</code></pre>
<p>You still have to materialize the final result of shape <code>[rows, xlim]</code>, which can<br>
be large if <code>xlim</code> is large, but you don’t have to materialize the full one-hot<br>
tensor of shape <code>[rows, columns, xlim]</code>, which is a factor of <code>columns</code> larger.</p>
<p>Best.</p>
<p>K. Frank</p>

username: lyprince
cooked: <p>Thanks <a class="mention" href="/u/kfrank">@KFrank</a></p>
<p>I also tried using <code>torch.vmap</code>, which is faster than <code>one_hot</code> but slower than flattening+offsetting.</p>
<p>With version 2.0.1 I get the following warning:</p>
<pre><code class="lang-bash">UserWarning: There is a performance drop because we have not yet implemented the batching rule for aten::bincount. Please file us an issue on GitHub so that we can prioritize its implementation. (Triggered internally at ../aten/src/ATen/functorch/BatchedFallback.cpp:82.)
</code></pre>