<p dir="auto"><code class="notranslate">DatetimeIndex._maybe_cast_slice_bound</code> can return the wrong value when you have an index that is monotonically decreasing, but not <em>strictly</em> monotonically decreasing.</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="In [1]: import pandas as pd

In [2]: pd.DatetimeIndex(['2017', '2017'])
Out[2]: DatetimeIndex(['2017-01-01', '2017-01-01'], dtype='datetime64[ns]', freq=None)

In [3]: pd.DatetimeIndex(['2017', '2017'])._maybe_cast_slice_bound('2017-01-01', 'left', 'loc')
Out[3]: Timestamp('2017-01-01 23:59:59.999999999')"><pre class="notranslate"><span class="pl-v">In</span> [<span class="pl-c1">1</span>]: <span class="pl-s1">import</span> <span class="pl-s1">pandas</span> <span class="pl-k">as</span> <span class="pl-s1">pd</span>

<span class="pl-v">In</span> [<span class="pl-c1">2</span>]: <span class="pl-s1">pd</span>.<span class="pl-v">DatetimeIndex</span>([<span class="pl-s">'2017'</span>, <span class="pl-s">'2017'</span>])
<span class="pl-v">Out</span>[<span class="pl-c1">2</span>]: <span class="pl-v">DatetimeIndex</span>([<span class="pl-s">'2017-01-01'</span>, <span class="pl-s">'2017-01-01'</span>], <span class="pl-s1">dtype</span><span class="pl-c1">=</span><span class="pl-s">'datetime64[ns]'</span>, <span class="pl-s1">freq</span><span class="pl-c1">=</span><span class="pl-c1">None</span>)

<span class="pl-v">In</span> [<span class="pl-c1">3</span>]: <span class="pl-s1">pd</span>.<span class="pl-v">DatetimeIndex</span>([<span class="pl-s">'2017'</span>, <span class="pl-s">'2017'</span>]).<span class="pl-en">_maybe_cast_slice_bound</span>(<span class="pl-s">'2017-01-01'</span>, <span class="pl-s">'left'</span>, <span class="pl-s">'loc'</span>)
<span class="pl-v">Out</span>[<span class="pl-c1">3</span>]: <span class="pl-v">Timestamp</span>(<span class="pl-s">'2017-01-01 23:59:59.999999999'</span>)</pre></div>
<p dir="auto">The issue is <a href="https://github.com/pandas-dev/pandas/blob/b0d9ee0b4c0911e089d4dd7c6a676557909fb7da/pandas/core/indexes/datetimes.py#L1469">here</a> That check really needs to check for strict monotonic decreasing, not just monotonic decreasing.</p>
<p dir="auto">Have we hit any other needs for strict monotonic algos? This is such an extreme edge case, not sure it's worthwhile on its own.</p>
<p dir="auto">Came up in <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="231659280" data-permission-text="Title is private" data-url="https://github.com/dask/dask/issues/2389" data-hovercard-type="issue" data-hovercard-url="/dask/dask/issues/2389/hovercard" href="https://github.com/dask/dask/issues/2389">dask/dask#2389</a></p>
<p dir="auto">Writing a dataframe to csv using <code class="notranslate">df.to_csv("/path/to/file.csv")</code> causes the creation of an "unnamed" field containing the indexes of the rows. Continually writing/reading to/from this file will result in many "unnamed" fields. Once there are three unnamed fields you can no longer use loc to replace values, what's more, it fails silently.</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="In [2]: df = DataFrame({'A': [1,2,3],'B': [4,5,501]})

In [3]: df
Out[3]:
   A    B
0  1    4
1  2    5
2  3  501

In [4]: df.B.loc[df.B &gt; 500]
Out[4]:
2    501
Name: B, dtype: int64

In [5]: df.B.loc[df.B &gt; 500] = None

In [6]: df
Out[6]:
   A    B
0  1  4.0
1  2  5.0
2  3  NaN
"><pre class="notranslate"><code class="notranslate">In [2]: df = DataFrame({'A': [1,2,3],'B': [4,5,501]})

In [3]: df
Out[3]:
   A    B
0  1    4
1  2    5
2  3  501

In [4]: df.B.loc[df.B &gt; 500]
Out[4]:
2    501
Name: B, dtype: int64

In [5]: df.B.loc[df.B &gt; 500] = None

In [6]: df
Out[6]:
   A    B
0  1  4.0
1  2  5.0
2  3  NaN

</code></pre></div>
<p dir="auto">So far so good, I was able to replace all values in <code class="notranslate">df.B</code> with <code class="notranslate">NaN</code>. I then write this out and read it back in</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="
In [7]: df.to_csv(&quot;./test.csv&quot;)

In [8]: df = read_csv(&quot;./test.csv&quot;)

In [9]: df.columns
Out[9]: Index([u'Unnamed: 0', u'A', u'B'], dtype='object')

In [10]: df
Out[10]:
   Unnamed: 0  A    B
0           0  1  4.0
1           1  2  5.0
2           2  3  NaN"><pre class="notranslate"><code class="notranslate">
In [7]: df.to_csv("./test.csv")

In [8]: df = read_csv("./test.csv")

In [9]: df.columns
Out[9]: Index([u'Unnamed: 0', u'A', u'B'], dtype='object')

In [10]: df
Out[10]:
   Unnamed: 0  A    B
0           0  1  4.0
1           1  2  5.0
2           2  3  NaN
</code></pre></div>
<p dir="auto">As you can see, this has create an unnamed field, but let's continue</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="In [14]: df.B.fillna(501, inplace=True)
"><pre class="notranslate"><code class="notranslate">In [14]: df.B.fillna(501, inplace=True)

</code></pre></div>
<p dir="auto">This is jsut to get 501 in place of the <code class="notranslate">NaN</code> I created earlier which I forgot to do before writing out</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="In [15]: df.B
Out[15]:
0      4.0
1      5.0
2    501.0
Name: B, dtype: float64

In [16]: df.B.loc[df.B &gt; 500]
Out[16]:
2    501.0
Name: B, dtype: float64

In [17]: df.B.loc[df.B &gt; 500] = None

...SettingWithCopyWarning...

In [18]: df.B
Out[18]:
0    4.0
1    5.0
2    NaN
Name: B, dtype: float64
"><pre class="notranslate"><code class="notranslate">In [15]: df.B
Out[15]:
0      4.0
1      5.0
2    501.0
Name: B, dtype: float64

In [16]: df.B.loc[df.B &gt; 500]
Out[16]:
2    501.0
Name: B, dtype: float64

In [17]: df.B.loc[df.B &gt; 500] = None

...SettingWithCopyWarning...

In [18]: df.B
Out[18]:
0    4.0
1    5.0
2    NaN
Name: B, dtype: float64

</code></pre></div>
<p dir="auto">Everything working fine</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="
In [19]: df.fillna(501, inplace=True)

In [20]: df.to_csv(&quot;./test.csv&quot;)

In [21]: df = read_csv(&quot;./test.csv&quot;)

In [22]: df.columns
Out[22]: Index([u'Unnamed: 0', u'Unnamed: 0.1', u'A', u'B'], dtype='object')

In [23]: df
Out[23]:
   Unnamed: 0  Unnamed: 0.1  A      B
0           0             0  1    4.0
1           1             1  2    5.0
2           2             2  3  501.0
"><pre class="notranslate"><code class="notranslate">
In [19]: df.fillna(501, inplace=True)

In [20]: df.to_csv("./test.csv")

In [21]: df = read_csv("./test.csv")

In [22]: df.columns
Out[22]: Index([u'Unnamed: 0', u'Unnamed: 0.1', u'A', u'B'], dtype='object')

In [23]: df
Out[23]:
   Unnamed: 0  Unnamed: 0.1  A      B
0           0             0  1    4.0
1           1             1  2    5.0
2           2             2  3  501.0

</code></pre></div>
<p dir="auto">Writing and reading again creates a 2nd unnamed field</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="
In [24]: df.B.loc[df.B &gt; 500]
Out[24]:
2    501.0
Name: B, dtype: float64

In [25]: df.B.loc[df.B &gt; 500] = None

In [26]: df.B
Out[26]:
0    4.0
1    5.0
2    NaN
Name: B, dtype: float64
"><pre class="notranslate"><code class="notranslate">
In [24]: df.B.loc[df.B &gt; 500]
Out[24]:
2    501.0
Name: B, dtype: float64

In [25]: df.B.loc[df.B &gt; 500] = None

In [26]: df.B
Out[26]:
0    4.0
1    5.0
2    NaN
Name: B, dtype: float64

</code></pre></div>
<p dir="auto">Which is no problem, everything still works so far...however</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="
In [27]: df.fillna(501, inplace=True)

In [28]: df
Out[28]:
   Unnamed: 0  Unnamed: 0.1  A      B
0           0             0  1    4.0
1           1             1  2    5.0
2           2             2  3  501.0

In [29]: df.to_csv(&quot;./test.csv&quot;)

In [30]: df = read_csv(&quot;./test.csv&quot;)

In [31]: df.columns
Out[31]: Index([u'Unnamed: 0', u'Unnamed: 0.1', u'Unnamed: 0.1', u'A', u'B'], dtype='object')

In [32]: df
Out[32]:
   Unnamed: 0  Unnamed: 0.1  Unnamed: 0.1  A      B
0           0             0             0  1    4.0
1           1             1             1  2    5.0
2           2             2             2  3  501.0
"><pre class="notranslate"><code class="notranslate">
In [27]: df.fillna(501, inplace=True)

In [28]: df
Out[28]:
   Unnamed: 0  Unnamed: 0.1  A      B
0           0             0  1    4.0
1           1             1  2    5.0
2           2             2  3  501.0

In [29]: df.to_csv("./test.csv")

In [30]: df = read_csv("./test.csv")

In [31]: df.columns
Out[31]: Index([u'Unnamed: 0', u'Unnamed: 0.1', u'Unnamed: 0.1', u'A', u'B'], dtype='object')

In [32]: df
Out[32]:
   Unnamed: 0  Unnamed: 0.1  Unnamed: 0.1  A      B
0           0             0             0  1    4.0
1           1             1             1  2    5.0
2           2             2             2  3  501.0

</code></pre></div>
<p dir="auto">We now have 3 unnamed fields</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="
In [33]: df.B.loc[df.B &gt; 500]
Out[33]:
2    501.0
Name: B, dtype: float64

In [34]: df.B.loc[df.B &gt; 500] = None

In [35]: df.B
Out[35]:
0      4.0
1      5.0
2    501.0
Name: B, dtype: float64"><pre class="notranslate"><code class="notranslate">
In [33]: df.B.loc[df.B &gt; 500]
Out[33]:
2    501.0
Name: B, dtype: float64

In [34]: df.B.loc[df.B &gt; 500] = None

In [35]: df.B
Out[35]:
0      4.0
1      5.0
2    501.0
Name: B, dtype: float64
</code></pre></div>
<p dir="auto">The method of replacing all values over 500 with <code class="notranslate">Nan</code> no longer works but also throws no errors or warnings.</p>
<p dir="auto">You CAN get around this using <code class="notranslate">df.loc[df.B &gt; 500, 'B'] = None</code> but obviously you shouldn't have to.</p>