Behavior of torch.unique

username: sk-g
cooked: <p>I expected either <code>torch.unique</code> or <code>torch.unique_consecutive</code> to give a tensor of unique elements instead, I get a random shuffle of the elements.</p>
<p>Here is what I did , please let me know if this is the correct way of invoking things.</p>
<pre><code class="lang-auto">a = torch.tensor([[3,1,3,3,4], [2,1,4,3,1]], dtype=torch.long)
output = torch.unique(a, sorted=True, dim=1)
output = torch.unique_consecutive(output, dim=1)
a.shape, output.shape, output
(torch.Size([2, 5]),
 torch.Size([2, 5]),
 tensor([[1, 3, 3, 3, 4],
         [1, 2, 3, 4, 1]]))
</code></pre>
<p>Ok, fine, chaining maybe bad (but it is not, really, <code>unique_consecutive</code> expects duplicate elements to be consecutive which I believe is achieved by <code>sorted=True</code>. Well, probably sorting is done on the same dim. So this is a mystery but ok.</p>
<p>With the same <code>a = torch.tensor([[3,1,3,3,4], [2,1,4,3,1]], dtype=torch.long)</code></p>
<p><code>output = torch.unique(a, dim=1)</code> yields</p>
<pre><code class="lang-auto">tensor([[1, 3, 3, 3, 4],
        [1, 2, 3, 4, 1]])
</code></pre>
<p>and <code>output = torch.unique(a, dim=0)</code> yields</p>
<pre><code class="lang-auto">tensor([[1, 3, 3, 3, 4],
        [1, 2, 3, 4, 1]])
</code></pre>
<p>Clearly I am misunderstanding the usage of <code>torch.unique</code></p>
<p>I expected to achieve something like <code>[list(set(x.tolist())) for x in a]</code> which clearly cannot be a tensor because not all dimensions would have same number of unique elements. So if the return type is a tensor, somewhere something is casting/padding additional elements  to satisfy tensor type return, which of course beats the purpose as the elements are now repeated.</p>
<p>In the simple example for <code>a</code>, such a unique operation yields -</p>
<pre><code class="lang-auto">z = [list(set(x.tolist())) for x in a]
z
[[1, 3, 4], [1, 2, 3, 4]]

</code></pre>
<p>Please advice.<br>
For now I have this work around (I need the unique indices in the order of appearance, I could use OrderedDict but that takes more space)</p>
<pre><code class="lang-auto">def unique(tensor):
    '''
    Returns a torch tensor with unique values from given tensor with order preserved.
    '''
    tensor_list = tensor.tolist()
    seen = set()
    unique_values = []
    for val in tensor_list:
        if val not in seen:
            seen.add(val)
            unique_values.append(val)
    return tensor.new(unique_values)
</code></pre>
<p>The problem with this function is that it expects the tensor to be 1-D so for a multi-dimensional tensor, I have to apply this in loop all of which are CPU operations and inherently would require loading big tensors as a copy because of <code>tolist()</code>, I was hoping to rely on CUDA based implementation but due to <code>sort</code> I cannot. Still,  it will be nice to at least have torch.unique() which does not return repeated values.</p>
<p>It will be of a great help if someone can update the docs with an example that would hit this problem. (Perhaps the same <code>a</code>?)</p>
<p>If I am in the wrong in understanding the way to use these two functions, please let me know, it will be a tremendous help, in this case, using a more complicated example in the docs would further help others.</p>
<p>Many thanks!</p>

username: ptrblck
cooked: <p><code>torch.unique</code> with a <code>dim</code> argument will return the unique tensors in the specified dimension, while <code>dim=None</code> will treat the tensor as a flattened tensor.<br>
Have a look at this example:</p>
<pre><code class="lang-python">x = torch.tensor([[0, 0, 0],
                  [0, 0, 1],
                  [0, 0, 0],
                  [0 ,0, 1]])

print(torch.unique(x, dim=0))
&gt; tensor([[0, 0, 0],
        [0, 0, 1]])
print(torch.unique(x, dim=1))
&gt; tensor([[0, 0],
        [0, 1],
        [0, 0],
        [0, 1]])
print(torch.unique(x))
&gt; tensor([0, 1])
</code></pre>
<p>If you want to get the flattened unique values for each row, you would need to use a loop, as <code>torch.unique</code> won’t add padding to the result.</p>

username: sk-g
cooked: <p>Thank you very much! So I was correct in understanding that there is padding.</p>
<p>While this padding is required for building a tensor, it is quite misleading to see repeats. Updated the example in docs with your example above would be really really really helpful.</p>
<p>For my use case, I will stick to my implementation then as there is no way but to use a loop to avoid padded duplicates. This is mostly because I need the ordered unique. However, the alternative would be to get the relative indices and then shuffle.</p>

username: ptrblck
cooked: <aside class="quote no-group" data-username="sk-g" data-post="3" data-topic="85300">
<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/sk-g/48/25518_2.png" class="avatar"> sk-g:</div>
<blockquote>
<p>So I was correct in understanding that there is padding.</p>
</blockquote>
</aside>
<p>I don’t see where the padding is.<br>
My code snippet returns the unique rows, then the unique columns, and finally the unique scalars.<br>
Where are these tensors potentially padded?</p>

username: sk-g
cooked: <p>You are right, I read your first comment more carefully after reading this in your second</p>
<p><code>My code snippet returns the unique rows, then the unique columns, and finally the unique scalars.</code></p>
<p>Essentially using dim I can select unique rows or unique dimension as a whole. This has nothing to do with padding!!  So the usage gives “unique rows”  and not  “here are the unique elements of each row”</p>
<p>What I should be looking at is the scalars themselves within each dimension which is to say what are the unique scalars in each entry of the dimension specified.</p>
<p>Thanks, I now understand things better.</p>