Fix scalar iloc
State: closed

 - [x] closes #15686
 - [x] tests added / passed
 - [x] passes ``git diff master --name-only -- '*.py' | flake8 --diff``
 - [x] whatsnew entry

Unless I'm missing anything, all code for indexers (inside ``pandas/core/indexing.py``) resorts to one of the following for setting values:
- if we can work directly on ``BlockManager.setitem`` (simple case, e.g. no multiple dtypes involved), do so
- otherwise, rely on methods provided by the object (mainly ``__setitem__``), _which are label based_

This makes it impossible, in the "non-simple case", to index positionally in the correct way with duplicated labels (by the way, it's also plain ugly).

I can see three (four) possible solutions:
1. temporarily strip an object from (some of) its labels - what this PR does. Arguably the simplest approach (doesn't require changing objects/``BlockManager`` code), arguably not very elegant (although we could already expect some efficiency gains compared to current master, since this removes some equality checks on labels)
2. add methods to the different objects that do position-based setting. Not trivial as it sounds, I'm afraid - such methods should talk directly the the ``BlockManager``s, hence for instance taking care of dtype changes
3. seriously improving ``BlockManager.setitem`` so that it is able to take care of multiple dtypes. Probably the best approach (would allow to enormously simplify indexing code), and the one requiring most work
4. (completely rewrite all setting code in ``indexing.py`` currently relying on ``self.obj.__setitem__`` so it directly talks to ``BlockManager.setitem``)

Notice that ``BlockManager.setitem`` must be fixed in order to fix #12991, but the fix for that bug is significantly simpler than 3. (it is just a matter of shifting indices, not of working with different dtypes).

The current PR... works, and does clean a bit the ``_setter`` interface (i.e. concerning the difference between ``iloc`` and ``loc``), which might be a good thing even in case we later decide to drop the less elegant ``InfoCleaner``. My preferred approach would be to apply it, then start working on 3., then come back to the indexing code and simplify it by profiting of 3. But if you prefer to start directly with 2. or 3., I can have a try in that direction. I don't think I'm willing to try 4. because current indexing code is already too complicated.

Labels:
name: Indexing
description: Related to indexing on series/frames, not to indexes themselves
name: Internals
description: Related to non-user accessible pandas implementation

Comments:
# [Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/15778?src=pr&el=h1) Report
> Merging [#15778](https://codecov.io/gh/pandas-dev/pandas/pull/15778?src=pr&el=desc) into [master](https://codecov.io/gh/pandas-dev/pandas/commit/0ea0f25bfb920cd0f58872dc95913a7aa9c0c3da?src=pr&el=desc) will **increase** coverage by `0.61%`.
> The diff coverage is `93.87%`.

[![Impacted file tree graph](https://codecov.io/gh/pandas-dev/pandas/pull/15778/graphs/tree.svg?width=650&height=150&src=pr&token=eZ4WkYLtcO)](https://codecov.io/gh/pandas-dev/pandas/pull/15778?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master   #15778      +/-   ##
==========================================
+ Coverage   90.38%      91%   +0.61%     
==========================================
  Files         161      143      -18     
  Lines       50916    49433    -1483     
==========================================
- Hits        46021    44985    -1036     
+ Misses       4895     4448     -447
```

| Flag | Coverage Δ | |
|---|---|---|
| #multiple | `?` | |
| #single | `?` | |

| [Impacted Files](https://codecov.io/gh/pandas-dev/pandas/pull/15778?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pandas/core/indexing.py](https://codecov.io/gh/pandas-dev/pandas/pull/15778?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvaW5kZXhpbmcucHk=) | `94.18% <100%> (+0.17%)` | :arrow_up: |
| [pandas/types/cast.py](https://codecov.io/gh/pandas-dev/pandas/pull/15778?src=pr&el=tree#diff-cGFuZGFzL3R5cGVzL2Nhc3QucHk=) | `85.04% <72.72%> (ø)` | |
| [pandas/io/gbq.py](https://codecov.io/gh/pandas-dev/pandas/pull/15778?src=pr&el=tree#diff-cGFuZGFzL2lvL2dicS5weQ==) | `25% <0%> (-58.34%)` | :arrow_down: |
| [pandas/tools/plotting.py](https://codecov.io/gh/pandas-dev/pandas/pull/15778?src=pr&el=tree#diff-cGFuZGFzL3Rvb2xzL3Bsb3R0aW5nLnB5) | `71.77% <0%> (-10.05%)` | :arrow_down: |
| [pandas/types/concat.py](https://codecov.io/gh/pandas-dev/pandas/pull/15778?src=pr&el=tree#diff-cGFuZGFzL3R5cGVzL2NvbmNhdC5weQ==) | `98.06% <0%> (-1.94%)` | :arrow_down: |
| [pandas/conftest.py](https://codecov.io/gh/pandas-dev/pandas/pull/15778?src=pr&el=tree#diff-cGFuZGFzL2NvbmZ0ZXN0LnB5) | `94.11% <0%> (-1.72%)` | :arrow_down: |
| [pandas/compat/pickle\_compat.py](https://codecov.io/gh/pandas-dev/pandas/pull/15778?src=pr&el=tree#diff-cGFuZGFzL2NvbXBhdC9waWNrbGVfY29tcGF0LnB5) | `68.29% <0%> (-1.22%)` | :arrow_down: |
| [pandas/tools/hashing.py](https://codecov.io/gh/pandas-dev/pandas/pull/15778?src=pr&el=tree#diff-cGFuZGFzL3Rvb2xzL2hhc2hpbmcucHk=) | `99.02% <0%> (-0.98%)` | :arrow_down: |
| [pandas/io/packers.py](https://codecov.io/gh/pandas-dev/pandas/pull/15778?src=pr&el=tree#diff-cGFuZGFzL2lvL3BhY2tlcnMucHk=) | `87.57% <0%> (-0.94%)` | :arrow_down: |
| [pandas/core/base.py](https://codecov.io/gh/pandas-dev/pandas/pull/15778?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvYmFzZS5weQ==) | `95.48% <0%> (-0.7%)` | :arrow_down: |
| ... and [189 more](https://codecov.io/gh/pandas-dev/pandas/pull/15778?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/15778?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/15778?src=pr&el=footer). Last update [0ea0f25...2a37bf6](https://codecov.io/gh/pandas-dev/pandas/pull/15778?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

(I'm trying to make a sense out of failed tests... in vain: nodes 0 and 2 fail reliably, but the difference between 2 and 3, which succeeds, should just be the python version - but 3.5 succeeds in TravisCI - and the locale)

I will investigate this later
@toobaz I would proceed in a slightly different manner here.

The key is to make *another* method in block manager that sets that is indexer based. This is like what ``.take`` does but for setting.

3) doesn't really matter atm, that's another issue.

I would start simple and w/o actually trying to fix an existing bug, but just trying to make positional based indexing work directly on a block.


> The key is to make another method in block manager that sets that is indexer based

Sorry, I probably wasn't clear: ``BlockManager.setitem`` _is_ position based, but it only works in simple cases (it is already used by indexing code in those cases). This is why 3. is a possible solution: making it more general.
(or if you are just saying "let's leave ``setitem`` as it is and restart from scratch": fine then, but the new method will presumably still rely on ``Block.setitem()``)
> (or if you are just saying "let's leave setitem as it is and restart from scratch": fine then, but the new 

this would likely be quite tricky. yes its the right thing to do, but covering all of the existing cases is hard. let's shoot for a simpler goal of simplifying code gradually as much as possible.

best way to do that is NOT to add new functionailty / fixes, rather to simplify cases that are existing in a more general way, w/o increasing the complexity.
> best way to do that is NOT to add new functionailty / fixes, rather to simplify cases that are existing in a more general way, w/o increasing the complexity.

Sure I agree in principle, but in practice?

> another method in block manager that sets that is indexer based

I'm just asking: why another one instead than improving ``BlockManager.setitem``?
> I'm just asking: why another one instead than improving BlockManager.setitem?

Well, after all I don't mind too much. Just tell me how to call a method that...

> is like what .take does but for setting.

and I will try to write it.






> I'm just asking: why another one instead than improving BlockManager.setitem?

no this is ok too.

would need a rebase
Rebased, but I'm [still](https://github.com/pandas-dev/pandas/pull/15778#issuecomment-288377915) unable to make sense of the failing tests (and I don't have time to investigate now)
closing as stale. if you want to re-visit, pls comment and we can re-open.