Repository: plotly/plotly.py

Issue:
Annotated heatmaps: font colour blends with the colour of the square.
When using the default color scheme, the font color contrasts normally with the fill of the each heatmap square. But if you try other standard color schemes with `reversescale=True`, when values are close to zero, the colors will merge. I think this is the wrong default behavior.


Discussion:
Hi @PlatonB, thanks for the report.

Could you include a small example, along with the version of plotly.py that you're using?  I'm mostly interested in whether you're using 3.4.0+, which includes the fixes from https://github.com/plotly/plotly.py/pull/1251. Thanks!
Hello, @jonmmease ! I am using version 3.4.2. An example of the output: https://drive.google.com/file/d/1ISG1TQIqKS5Aj5tEWKlP9QzSuNvxAQK5/view?usp=sharing
Thanks for the version info @PlatonB.  Could you include a small example? The data itself isn't important, but the way you're specifying your colorscale may be.
@jonmmease 
Example:
https://github.com/PlatonB/ld_triangle/blob/e8345b615c59c74f9c2581c8f28a16ec318a3ae5/LD_triangle.py#L328
Colorscale used most often to reproduce the error - Greens.
Hi @PlatonB,

I don't think I'm able to reproduce what you're seeing.  Here's what I tried:

```python
# Imports and data
from plotly.offline import iplot, init_notebook_mode
import plotly.figure_factory as ff
init_notebook_mode()

z = [[.1, .3, .5, .7, .9],  
     [1, .8, .6, .4, .2],
     [.2, 0, .5, .7, .9],  
     [.9, .8, .4, .2, 0],
     [.3, .4, .5, .7, 1]] 

# Standard scale direction
fig = ff.create_annotated_heatmap(z, colorscale='Greens', showscale=True)
iplot(fig)
```
![newplot 30](https://user-images.githubusercontent.com/15064365/50042622-a6022000-0033-11e9-8446-8be83137e435.png)

```python
# Reversed scale direction
fig = ff.create_annotated_heatmap(z, colorscale='Greens', reversescale=True, showscale=True)
iplot(fig)
```
![newplot 31](https://user-images.githubusercontent.com/15064365/50042630-b4503c00-0033-11e9-9f3f-f675d4f5b618.png)

```python
import plotly
print(plotly.__version__)
```
```3.4.2```

Could you try reproducing the issue by starting with this simple example?
-Jon

Hello, @jonmmease !
With your data the error was not reproduced.
Figure object with my data:
[Contrast_bug_fig_object.txt](https://github.com/plotly/plotly.py/files/2683002/Contrast_bug_fig_object.txt)
Hi @PlatonB,

Sorry for not following up on this sooner.  If you're still interested in working through this, what we need is an example of the input to the figure factory that causes the issue.  Basically, can you start with my example above and then change the `z` parameter to `create_annotated_heatmap` so that it reproduces the behavior you're seeing?
Hello, @jonmmease 
Example of 50x50 input matrix:
https://drive.google.com/file/d/1pzwFEO9-PtjXsqg2wfhS-XIOIWpod_7z/view?usp=sharing

Also, in the previous post I attached an example of a complete Figure object, that contains 30x30 z.
Hi @PlatonB, thanks for providing the dataset,

here's what I see using the if I use you're dataset with `reversescale=True`
```python
# Imports and data
from plotly.offline import iplot, init_notebook_mode
import plotly.figure_factory as ff
import numpy as np
import json
init_notebook_mode()

with open('z.json', 'r') as f:
    z = json.load(f)

# Standard scale direction
fig = ff.create_annotated_heatmap(z, colorscale='Greens', showscale=True, reversescale=True)
fig.layout.width = 1200
fig.layout.height = 1200
iplot(fig)
```
![newplot 5](https://user-images.githubusercontent.com/15064365/51115225-9eba6780-17d5-11e9-931b-2a5632a65a22.png)

Which has a lot of overlap, but I think I can make out some pixels with white text that should be black.  Here are two subsets of z that show some interesting behavior.

`[1:13, 0:8]`
```python
# Imports and data
from plotly.offline import iplot, init_notebook_mode
import plotly.figure_factory as ff
import numpy as np
import json
init_notebook_mode()

with open('z.json', 'r') as f:
    z = json.load(f)
    
import numpy as np
sub_z = np.array(z)[1:13, 0:8].tolist()

# Standard scale direction
fig = ff.create_annotated_heatmap(sub_z, colorscale='Greens', showscale=True, reversescale=True)
fig.layout.width = 800
fig.layout.height = 800
iplot(fig)
```
![newplot 6](https://user-images.githubusercontent.com/15064365/51116100-1f7a6300-17d8-11e9-8477-e5399b33fec2.png)


`[1:13, 1:8]`
```python
# Imports and data
from plotly.offline import iplot, init_notebook_mode
import plotly.figure_factory as ff
import numpy as np
import json
init_notebook_mode()

with open('z.json', 'r') as f:
    z = json.load(f)
    
import numpy as np
sub_z = np.array(z)[1:13, 1:8].tolist()

# Standard scale direction
fig = ff.create_annotated_heatmap(sub_z, colorscale='Greens', showscale=True, reversescale=True)
fig.layout.width = 800
fig.layout.height = 800
iplot(fig)
```
![newplot 7](https://user-images.githubusercontent.com/15064365/51116117-315c0600-17d8-11e9-8d02-4f4659d9ebf1.png)

The `[1:13, 0:8]` subset has a pixel on the top right that certainly looks like it should have black text.  And if the first column is removed and we look at the `[1:13, 1:8]` subset then the pixel text is colored black.

So yeah, looks like a bug.  Thanks again for the report!


Resolving patch:
diff --git a/plotly/figure_factory/_annotated_heatmap.py b/plotly/figure_factory/_annotated_heatmap.py
--- a/plotly/figure_factory/_annotated_heatmap.py
+++ b/plotly/figure_factory/_annotated_heatmap.py
@@ -1,4 +1,4 @@
-from __future__ import absolute_import
+from __future__ import absolute_import, division
 
 from plotly import exceptions, optional_imports
 import plotly.colors as clrs
@@ -234,8 +234,8 @@ def get_z_mid(self):
             z_min = np.amin(self.z)
             z_max = np.amax(self.z)
         else:
-            z_min = min(min(self.z))
-            z_max = max(max(self.z))
+            z_min = min([v for row in self.z for v in row])
+            z_max = max([v for row in self.z for v in row])
         z_mid = (z_max+z_min) / 2
         return z_mid
 


Test patch:
diff --git a/plotly/tests/test_optional/test_tools/test_figure_factory.py b/plotly/tests/test_optional/test_tools/test_figure_factory.py
--- a/plotly/tests/test_optional/test_tools/test_figure_factory.py
+++ b/plotly/tests/test_optional/test_tools/test_figure_factory.py
@@ -773,7 +773,7 @@ def test_simple_annotated_heatmap(self):
                                         'xref': 'x',
                                         'y': 0,
                                         'yref': 'y'},
-                                       {'font': {'color': '#FFFFFF'},
+                                       {'font': {'color': '#000000'},
                                         'showarrow': False,
                                         'text': '0.5',
                                         'x': 2,
@@ -872,7 +872,7 @@ def test_annotated_heatmap_kwargs(self):
                                   'xref': 'x',
                                   'y': 'Three',
                                   'yref': 'y'},
-                                 {'font': {'color': '#000000'},
+                                 {'font': {'color': '#FFFFFF'},
                                   'showarrow': False,
                                   'text': 'sixth',
                                   'x': 'B',
@@ -951,7 +951,7 @@ def test_annotated_heatmap_reversescale(self):
                            'xref': 'x',
                            'y': 'Three',
                            'yref': 'y'},
-                          {'font': {'color': '#FFFFFF'},
+                          {'font': {'color': '#000000'},
                            'showarrow': False,
                            'text': 'sixth',
                            'x': 'B',
@@ -972,6 +972,91 @@ def test_annotated_heatmap_reversescale(self):
         self.assert_fig_equal(a['layout'],
                               expected_a['layout'])
 
+    def test_bug_1300(self):
+        # https://github.com/plotly/plotly.py/issues/1300
+        sub_z = [
+            [0.1, 0.0, 0.0],
+            [0.0, 1.0, 0.1]]
+
+        # sub_z = sub_z.tolist()
+
+        # Standard scale direction
+        fig = ff.create_annotated_heatmap(
+            sub_z, colorscale='Greens', showscale=True, reversescale=True)
+
+        expected = graph_objs.Figure({
+            'data': [{'colorscale': 'Greens',
+                      'reversescale': True,
+                      'showscale': True,
+                      'type': 'heatmap',
+                      'uid': 'baeae9f0-d650-4507-99ba-97226bb8fe6c',
+                      'z': [[0.1, 0., 0.],
+                            [0.,  1., 0.1]]}],
+            'layout': {
+                'annotations': [
+                    {'font': {'color': '#000000'},
+                     'showarrow': False,
+                     'text': '0.1',
+                     'x': 0,
+                     'xref': 'x',
+                     'y': 0,
+                     'yref': 'y'},
+                    {'font': {'color': '#000000'},
+                     'showarrow': False,
+                     'text': '0.0',
+                     'x': 1,
+                     'xref': 'x',
+                     'y': 0,
+                     'yref': 'y'},
+                    {'font': {'color': '#000000'},
+                     'showarrow': False,
+                     'text': '0.0',
+                     'x': 2,
+                     'xref': 'x',
+                     'y': 0,
+                     'yref': 'y'},
+                    {'font': {'color': '#000000'},
+                     'showarrow': False,
+                     'text': '0.0',
+                     'x': 0,
+                     'xref': 'x',
+                     'y': 1,
+                     'yref': 'y'},
+                    {'font': {'color': '#FFFFFF'},
+                     'showarrow': False,
+                     'text': '1.0',
+                     'x': 1,
+                     'xref': 'x',
+                     'y': 1,
+                     'yref': 'y'},
+                    {'font': {'color': '#000000'},
+                     'showarrow': False,
+                     'text': '0.1',
+                     'x': 2,
+                     'xref': 'x',
+                     'y': 1,
+                     'yref': 'y'}
+                ],
+                'xaxis': {
+                    'gridcolor': 'rgb(0, 0, 0)',
+                    'showticklabels': False,
+                    'side': 'top',
+                    'ticks': ''},
+                'yaxis': {
+                    'showticklabels': False,
+                    'ticks': '',
+                    'ticksuffix': '  '}}
+        })
+
+        # Remove uids
+        for trace in fig.data:
+            trace.update(uid=None)
+        for trace in expected.data:
+            trace.update(uid=None)
+
+        # Perform comparison
+        self.assert_fig_equal(fig, expected)
+
 
 class TestTable(TestCase, NumpyTestUtilsMixin):