Repository: pandas-dev/pandas

Issue:
Default behavior of plot.bar changed to plot a different color for each bar
#### Code Sample, a copy-pastable example if possible
**BEFORE**
```python
df_india.groupby('sector').id.count().sort_values().plot.bar();
```
would result in a bar plot with all bars having the same color

**AFTER** version 0.20
```python
df_india.groupby('sector').id.count().sort_values().plot.bar(color='cornflowerblue');
```
I need to explicitly provide a color, else each bar is colored differently

Minimal reproducible code:

```
import pandas as pd
df = pd.DataFrame({'account-start': ['2017-02-03', '2017-03-03', '2017-01-01'],
                   'client': ['Alice Anders', 'Bob Baker', 'Charlie Chaplin'],
                   'balance': [-1432.32, 10.43, 30000.00],
                   'db-id': [1234, 2424, 251],
                   'proxy-id': [525, 1525, 2542],
                   'rank': [52, 525, 32],
                   })
df.client.value_counts().plot.bar()
```

Output:

![download](https://user-images.githubusercontent.com/3866405/38206924-d34dffcc-36c9-11e8-866c-57364f90042b.png)


#### Problem description
Why was the behavior of Series.plot.bar changed to plot bars with different color? Visually these colors add nothing to the plot as different colors should only be used when they correspond to differences of meaning in the data.

Why is the default behavior to provide an unnecessarily visually overwhelming graph? It took me some time to realize why my bars suddenly started acting strangely. Now, I pass color='cornflowerblue' to get all my bars the same pleasant hue.

Reference for visual appeal and the use of colors: http://www.perceptualedge.com/articles/visual_business_intelligence/rules_for_using_color.pdf

Would it be possible to revert to the pre-0.20 behavior?

#### Output of ``pd.show_versions()``

<details>

[paste the output of ``pd.show_versions()`` here below this line]

INSTALLED VERSIONS
------------------
commit: None
python: 3.6.4.final.0
python-bits: 64
OS: Linux
OS-release: 4.9.0-5-amd64
machine: x86_64
processor: 
byteorder: little
LC_ALL: C.UTF-8
LANG: C.UTF-8
LOCALE: en_US.UTF-8

pandas: 0.22.0
pytest: 3.2.1
pip: 9.0.1
setuptools: 38.5.2
Cython: 0.26.1
numpy: 1.14.2
scipy: 1.0.0
pyarrow: 0.8.0
xarray: 0.10.1
IPython: 6.1.0
sphinx: 1.6.3
patsy: 0.4.1
dateutil: 2.6.0
pytz: 2018.3
blosc: None
bottleneck: 1.2.1
tables: 3.4.2
numexpr: 2.6.2
feather: 0.4.0
matplotlib: 2.1.0
openpyxl: 2.4.8
xlrd: 1.1.0
xlwt: 1.3.0
xlsxwriter: 1.0.2
lxml: 4.1.0
bs4: 4.6.0
html5lib: 0.9999999
sqlalchemy: 1.1.13
pymysql: None
psycopg2: None
jinja2: 2.9.6
s3fs: None
fastparquet: None
pandas_gbq: None
pandas_datareader: 0.6.0
</details>



Discussion:
Do you have a reproducible example? This sounds like something that's been fixed, but it's hard to say without an example http://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports
Looks like a duplicate of https://github.com/pandas-dev/pandas/issues/18394
Hi @TomAugspurger ! Thanks for replying. It's reproducible on version ` 0.22.0` of pandas. Is it fixed in a later version?

Minimal reproducible code:

```
import pandas as pd
df = pd.DataFrame({'account-start': ['2017-02-03', '2017-03-03', '2017-01-01'],
                   'client': ['Alice Anders', 'Bob Baker', 'Charlie Chaplin'],
                   'balance': [-1432.32, 10.43, 30000.00],
                   'db-id': [1234, 2424, 251],
                   'proxy-id': [525, 1525, 2542],
                   'rank': [52, 525, 32],
                   })
df.client.value_counts().plot.bar()
```

Output:

![download](https://user-images.githubusercontent.com/3866405/38206924-d34dffcc-36c9-11e8-866c-57364f90042b.png)

@TomAugspurger This isn't really a duplicate of the mentioned issue. The other one was caused as a result of this, but is not complaining of the same aesthetic appeal issue I refer to above. It is more complaining about functionality (logy=true) not working due to this new change. Hence, I believe it is a different issue
I think they are related, but not the same issue. The issue I raise here is really one of visual design rather than broken functionality
Sorry about that, wasn't reading closely enough.

Thanks for the example. Could you edit your original post to include the example?

Are you interested in submitting a pull request to fix this?
no problem @TomAugspurger! I've updated the original post to include the example. I'd love to submit a PR for this as it's something that's been personally bothering me a lot :D 

However, I'm a bit new to the codebase so it may take a little while before I get the hang of stuff. I'll start digging around to see which portions of the code I need to touch for this. Is there a particular portion of the codebase you'd suggest I look at?
Thanks! The plotting code is a bit tricky, but hopefully not too bad.

I think the issue is lies around either

- `get_standard_colors`: https://github.com/pandas-dev/pandas/blob/fac2ef1b2095c7785006c901e941e2657571d935/pandas/plotting/_style.py#L17, or
- `MPLPlot._get_colors`: https://github.com/pandas-dev/pandas/blob/master/pandas/plotting/_core.py#L674-L680

I notice that `_get_standard_colors(num_colors=1)` doesn't return a single color, which I think is surprising

```python
(Pdb) _get_standard_colors(num_colors=1)
['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf', '#1f77b4']
```

So I'd say look through that and see if there's something fishy. Let me know if you get stuck, I haven't looked closely at it.

If `_get_standard_colors` is behaving correctly, then we may need to override `_get_colors` in `BarPlot`. But let's hold off on that till we know if ther'es a bug in `_get_standard_colors`.
This definitely looks like a bug in `_get_standard_colors` specifically in 
```
    if len(colors) != num_colors:
        try:
            multiple = num_colors // len(colors) - 1
        except ZeroDivisionError:
            raise ValueError("Invalid color argument: ''")
        mod = num_colors % len(colors)

        colors += multiple * colors
        colors += colors[:mod]
```
It is trying to increase the size of colors array in case number of items are less but does not handle case when size of colors array is larger than number of items asked. The side effect can be seen by calling (for any number less than 10 and result is 10 + number)
``` 
_get_standard_colors(num_colors=9)
Out[7]: 
['#1f77b4',
 '#ff7f0e',
 '#2ca02c',
 '#d62728',
 '#9467bd',
 '#8c564b',
 '#e377c2',
 '#7f7f7f',
 '#bcbd22',
 '#17becf',
 '#1f77b4',
 '#ff7f0e',
 '#2ca02c',
 '#d62728',
 '#9467bd',
 '#8c564b',
 '#e377c2',
 '#7f7f7f',
 '#bcbd22']
```

Resolving patch:
diff --git a/pandas/plotting/_core.py b/pandas/plotting/_core.py
--- a/pandas/plotting/_core.py
+++ b/pandas/plotting/_core.py
@@ -2172,7 +2172,9 @@ def boxplot(data, column=None, by=None, ax=None, fontsize=None,
         column = 'x'
 
     def _get_colors():
-        return _get_standard_colors(color=kwds.get('color'), num_colors=1)
+        #  num_colors=3 is required as method maybe_color_bp takes the colors
+        #  in positions 0 and 2.
+        return _get_standard_colors(color=kwds.get('color'), num_colors=3)
 
     def maybe_color_bp(bp):
         if 'color' not in kwds:
diff --git a/pandas/plotting/_style.py b/pandas/plotting/_style.py
--- a/pandas/plotting/_style.py
+++ b/pandas/plotting/_style.py
@@ -42,6 +42,8 @@ def _get_standard_colors(num_colors=None, colormap=None, color_type='default',
                                                list('bgrcmyk')))
             if isinstance(colors, compat.string_types):
                 colors = list(colors)
+
+            colors = colors[0:num_colors]
         elif color_type == 'random':
             import pandas.core.common as com
 


Test patch:
diff --git a/pandas/tests/plotting/test_misc.py b/pandas/tests/plotting/test_misc.py
--- a/pandas/tests/plotting/test_misc.py
+++ b/pandas/tests/plotting/test_misc.py
@@ -215,8 +215,8 @@ def test_parallel_coordinates_with_sorted_labels(self):
 
         df = DataFrame({"feat": [i for i in range(30)],
                         "class": [2 for _ in range(10)] +
-                        [3 for _ in range(10)] +
-                        [1 for _ in range(10)]})
+                                 [3 for _ in range(10)] +
+                                 [1 for _ in range(10)]})
         ax = parallel_coordinates(df, 'class', sort_labels=True)
         polylines, labels = ax.get_legend_handles_labels()
         color_label_tuples = \
@@ -310,6 +310,33 @@ def test_get_standard_colors_random_seed(self):
         color2 = _get_standard_colors(1, color_type='random')
         assert color1 == color2
 
+    def test_get_standard_colors_default_num_colors(self):
+        from pandas.plotting._style import _get_standard_colors
+
+        # Make sure the default color_types returns the specified amount
+        color1 = _get_standard_colors(1, color_type='default')
+        color2 = _get_standard_colors(9, color_type='default')
+        color3 = _get_standard_colors(20, color_type='default')
+        assert len(color1) == 1
+        assert len(color2) == 9
+        assert len(color3) == 20
+
+    def test_plot_single_color(self):
+        # Example from #20585. All 3 bars should have the same color
+        df = DataFrame({'account-start': ['2017-02-03', '2017-03-03',
+                                          '2017-01-01'],
+                        'client': ['Alice Anders', 'Bob Baker',
+                                   'Charlie Chaplin'],
+                        'balance': [-1432.32, 10.43, 30000.00],
+                        'db-id': [1234, 2424, 251],
+                        'proxy-id': [525, 1525, 2542],
+                        'rank': [52, 525, 32],
+                        })
+        ax = df.client.value_counts().plot.bar()
+        colors = lmap(lambda rect: rect.get_facecolor(),
+                      ax.get_children()[0:3])
+        assert all(color == colors[0] for color in colors)
+
     def test_get_standard_colors_no_appending(self):
         # GH20726