<p dir="auto">xref <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="43145877" data-permission-text="Title is private" data-url="https://github.com/pandas-dev/pandas/issues/8309" data-hovercard-type="issue" data-hovercard-url="/pandas-dev/pandas/issues/8309/hovercard" href="https://github.com/pandas-dev/pandas/issues/8309">#8309</a></p>
<p dir="auto">for example:</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="pd.qcut([1,1,1,1,1,1,1,1,1,1,1,1,1,5,5,5], [0.00001, 0.5])"><pre class="notranslate"><span class="pl-s1">pd</span>.<span class="pl-en">qcut</span>([<span class="pl-c1">1</span>,<span class="pl-c1">1</span>,<span class="pl-c1">1</span>,<span class="pl-c1">1</span>,<span class="pl-c1">1</span>,<span class="pl-c1">1</span>,<span class="pl-c1">1</span>,<span class="pl-c1">1</span>,<span class="pl-c1">1</span>,<span class="pl-c1">1</span>,<span class="pl-c1">1</span>,<span class="pl-c1">1</span>,<span class="pl-c1">1</span>,<span class="pl-c1">5</span>,<span class="pl-c1">5</span>,<span class="pl-c1">5</span>], [<span class="pl-c1">0.00001</span>, <span class="pl-c1">0.5</span>])</pre></div>
<p dir="auto">will raise "ValueError: Bin edges must be unique: array([ 1.,  1.])" exception</p>
<p dir="auto">Fix suggestion - add one new line:</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="def qcut(x, q, labels=None, retbins=False, precision=3):
    if com.is_integer(q):
        quantiles = np.linspace(0, 1, q + 1)
    else:
        quantiles = q
    bins = algos.quantile(x, quantiles)
---&gt;bins = np.unique(bins)
    return _bins_to_cuts(x, bins, labels=labels, retbins=retbins,
        precision=precision, include_lowest=True)"><pre class="notranslate"><span class="pl-k">def</span> <span class="pl-en">qcut</span>(<span class="pl-s1">x</span>, <span class="pl-s1">q</span>, <span class="pl-s1">labels</span><span class="pl-c1">=</span><span class="pl-c1">None</span>, <span class="pl-s1">retbins</span><span class="pl-c1">=</span><span class="pl-c1">False</span>, <span class="pl-s1">precision</span><span class="pl-c1">=</span><span class="pl-c1">3</span>):
    <span class="pl-k">if</span> <span class="pl-s1">com</span>.<span class="pl-en">is_integer</span>(<span class="pl-s1">q</span>):
        <span class="pl-s1">quantiles</span> <span class="pl-c1">=</span> <span class="pl-s1">np</span>.<span class="pl-en">linspace</span>(<span class="pl-c1">0</span>, <span class="pl-c1">1</span>, <span class="pl-s1">q</span> <span class="pl-c1">+</span> <span class="pl-c1">1</span>)
    <span class="pl-k">else</span>:
        <span class="pl-s1">quantiles</span> <span class="pl-c1">=</span> <span class="pl-s1">q</span>
    <span class="pl-s1">bins</span> <span class="pl-c1">=</span> <span class="pl-s1">algos</span>.<span class="pl-en">quantile</span>(<span class="pl-s1">x</span>, <span class="pl-s1">quantiles</span>)
<span class="pl-c1">-</span><span class="pl-c1">-</span><span class="pl-c1">-</span><span class="pl-c1">&gt;</span><span class="pl-s1">bins</span> <span class="pl-c1">=</span> <span class="pl-s1">np</span>.<span class="pl-en">unique</span>(<span class="pl-s1">bins</span>)
    <span class="pl-k">return</span> <span class="pl-en">_bins_to_cuts</span>(<span class="pl-s1">x</span>, <span class="pl-s1">bins</span>, <span class="pl-s1">labels</span><span class="pl-c1">=</span><span class="pl-s1">labels</span>, <span class="pl-s1">retbins</span><span class="pl-c1">=</span><span class="pl-s1">retbins</span>,
        <span class="pl-s1">precision</span><span class="pl-c1">=</span><span class="pl-s1">precision</span>, <span class="pl-s1">include_lowest</span><span class="pl-c1">=</span><span class="pl-c1">True</span>)</pre></div>
<p dir="auto">pandas.cut and similar operate on the principle that bins start at one end of a line and finish at another. However, sometimes it is necessary to work with data that operates on a circular scale, for example compass directions.</p>
<p dir="auto">It would be a great enhancement if pd.cut could deal with these cases.<br>
I do not know of anything in numpy or pandas that currently deals with this situation, but it would be very useful.</p>
<h4 dir="auto">Current workaround</h4>
<p dir="auto">Currently this is accomplished by having a bin covering half of the real bin at each end of the scale, for example the bin 345° to15° would be covered by a bin of 0° to 15° and a bin of 345° to 360°. It is then necessary to assign the members of one category to the other, rename it, and then delete the unused category.</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="data = pd.DataFrame(np.random.random(400) * 360, columns=[&quot;Direction&quot;])
bins = np.array([-15,  15,  45,  75, 105, 135, 165, 195, 225, 255, 285, 315, 345, 375])
c = pd.cut(data.Direction, bins)
c[c==&quot;(-15, 15]&quot;] = &quot;(345, 375]&quot;
c = c.cat.remove_unused_categories()
cats = list(c.cat.categories)
cats[-1] = '(345, 15]'
c = c.cat.set_categories(cats)"><pre class="notranslate"><span class="pl-s1">data</span> <span class="pl-c1">=</span> <span class="pl-s1">pd</span>.<span class="pl-v">DataFrame</span>(<span class="pl-s1">np</span>.<span class="pl-s1">random</span>.<span class="pl-en">random</span>(<span class="pl-c1">400</span>) <span class="pl-c1">*</span> <span class="pl-c1">360</span>, <span class="pl-s1">columns</span><span class="pl-c1">=</span>[<span class="pl-s">"Direction"</span>])
<span class="pl-s1">bins</span> <span class="pl-c1">=</span> <span class="pl-s1">np</span>.<span class="pl-en">array</span>([<span class="pl-c1">-</span><span class="pl-c1">15</span>,  <span class="pl-c1">15</span>,  <span class="pl-c1">45</span>,  <span class="pl-c1">75</span>, <span class="pl-c1">105</span>, <span class="pl-c1">135</span>, <span class="pl-c1">165</span>, <span class="pl-c1">195</span>, <span class="pl-c1">225</span>, <span class="pl-c1">255</span>, <span class="pl-c1">285</span>, <span class="pl-c1">315</span>, <span class="pl-c1">345</span>, <span class="pl-c1">375</span>])
<span class="pl-s1">c</span> <span class="pl-c1">=</span> <span class="pl-s1">pd</span>.<span class="pl-en">cut</span>(<span class="pl-s1">data</span>.<span class="pl-v">Direction</span>, <span class="pl-s1">bins</span>)
<span class="pl-s1">c</span>[<span class="pl-s1">c</span><span class="pl-c1">==</span><span class="pl-s">"(-15, 15]"</span>] <span class="pl-c1">=</span> <span class="pl-s">"(345, 375]"</span>
<span class="pl-s1">c</span> <span class="pl-c1">=</span> <span class="pl-s1">c</span>.<span class="pl-s1">cat</span>.<span class="pl-en">remove_unused_categories</span>()
<span class="pl-s1">cats</span> <span class="pl-c1">=</span> <span class="pl-en">list</span>(<span class="pl-s1">c</span>.<span class="pl-s1">cat</span>.<span class="pl-s1">categories</span>)
<span class="pl-s1">cats</span>[<span class="pl-c1">-</span><span class="pl-c1">1</span>] <span class="pl-c1">=</span> <span class="pl-s">'(345, 15]'</span>
<span class="pl-s1">c</span> <span class="pl-c1">=</span> <span class="pl-s1">c</span>.<span class="pl-s1">cat</span>.<span class="pl-en">set_categories</span>(<span class="pl-s1">cats</span>)</pre></div>
<h4 dir="auto">Desired behaviour</h4>
<p dir="auto">Better would be accepting a parameter indicating at what point the scale loops, e.g. zeroat=360. Other solutions may be possible, such as taking the structure of the list of bins to indicate that the scale loops.</p>
<p dir="auto">One issue is the order. How can the fact that 345 is both more than and less than 0 be dealt with, depending on what direction you are looking in? There is an order, but it isn't straight forward.</p>