Repository: pandas-dev/pandas

Issue:
NotImplementedError: 'AnnAssign' nodes are not implemented but numpy-like syntax works
My dataframe ``df`` has a column called "Unnamed:_6" which is of dtype ``object``.

Running the following command works just fine:
```python
df[df['Unnamed:_6'] == "Foo"]
```

However, the equivalent line:
```python
df.query('Unnamed:_6 == "Foo"')
```
raises the following exception:
```python
NotImplementedError: 'AnnAssign' nodes are not implemented
```



Discussion:
@simtsc Could you please attach a code to replicate your dataframe?
You'll also need to provide more information per the contributing guide:

https://pandas.pydata.org/pandas-docs/stable/contributing.html#bug-reports-and-enhancement-requests

i.e. pd.show_versions(). A quick google on the error makes me think it has something to do with annotations but you need to provide the extra information before this can be looked at in any detail
@WillAyd @simtsc 
Okay, I think I may have found the source of this problem. I tried to replicate the situation as mentioned in the issue and chalked out the following errors:

```python
>>> x=pd.DataFrame([['a','b','c'],['Foo','e','f']] , columns=['Unnamed:_6','guy','walks'])     
>>> x.query('Unnamed:_6 == "Foo"')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python2.7/dist-packages/pandas/core/frame.py", line 2297, in query
    res = self.eval(expr, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/pandas/core/frame.py", line 2366, in eval
    return _eval(expr, inplace=inplace, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/pandas/core/computation/eval.py", line 290, in eval
    truediv=truediv)
  File "/usr/local/lib/python2.7/dist-packages/pandas/core/computation/expr.py", line 732, in __init__
    self.terms = self.parse()
  File "/usr/local/lib/python2.7/dist-packages/pandas/core/computation/expr.py", line 749, in parse
    return self._visitor.visit(self.expr)
  File "/usr/local/lib/python2.7/dist-packages/pandas/core/computation/expr.py", line 310, in visit
    node = ast.fix_missing_locations(ast.parse(clean))
  File "/usr/lib/python2.7/ast.py", line 37, in parse
    return compile(source, filename, mode, PyCF_ONLY_AST)
  File "<unknown>", line 1
    Unnamed :_6 =="Foo"
            ^
SyntaxError: invalid syntax
```

Then I decided to work on another `column` name and got this: 

```python
>>> x=pd.DataFrame([['a','b','c'],['Foo','e','f']] , columns=['a:b','guy','walks'])     
>>> x
   a:b guy walks
0    a   b     c
1  Foo   e     f
>>> x.query('a:b == "Foo"')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python2.7/dist-packages/pandas/core/frame.py", line 2297, in query
    res = self.eval(expr, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/pandas/core/frame.py", line 2366, in eval
    return _eval(expr, inplace=inplace, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/pandas/core/computation/eval.py", line 290, in eval
    truediv=truediv)
  File "/usr/local/lib/python2.7/dist-packages/pandas/core/computation/expr.py", line 732, in __init__
    self.terms = self.parse()
  File "/usr/local/lib/python2.7/dist-packages/pandas/core/computation/expr.py", line 749, in parse
    return self._visitor.visit(self.expr)
  File "/usr/local/lib/python2.7/dist-packages/pandas/core/computation/expr.py", line 310, in visit
    node = ast.fix_missing_locations(ast.parse(clean))
  File "/usr/lib/python2.7/ast.py", line 37, in parse
    return compile(source, filename, mode, PyCF_ONLY_AST)
  File "<unknown>", line 1
    a :b =="Foo"
      ^
SyntaxError: invalid syntax

```

Apparently, the `:` in column name upsets this particular query.
**Note : Error type and message is different**
Since the error type and message are different I doubt that is the same. I'd wait until @simtsc can provide more information - otherwise this is a guessing game
Hi guys, sorry, I forgot to mention the version information, indeed. I am working in a jupyter notebook. I am running ``jupyter notebook server 5.5.0`` with ``Python 3.6.4 |Anaconda custom (64-bit)| (default, Jan 16 2018, 10:22:32) [MSC v.1900 64 bit (AMD64)]``. The input data is coming from an Excel file. However, I am not able to share the file here. This is the exact call I am using to read the file:
```python
df_dict = pd.read_excel(os.path.join('data', 'input.xlsm'), 
                        skiprows=14, usecols='G:Y', sheet_name=None) 
```

Here is what pandas gathers about my environment:
commit: None
python: 3.6.4.final.0
python-bits: 64
OS: Windows
OS-release: 10
machine: AMD64
processor: Intel64 Family 6 Model 94 Stepping 3, GenuineIntel
byteorder: little
LC_ALL: None
LANG: None
LOCALE: None.None

pandas: 0.23.1
pytest: 3.3.2
pip: 10.0.1
setuptools: 38.4.0
Cython: 0.27.3
numpy: 1.14.2
scipy: 1.0.0
pyarrow: None
xarray: None
IPython: 6.2.1
sphinx: 1.6.6
patsy: 0.5.0
dateutil: 2.6.1
pytz: 2017.3
blosc: None
bottleneck: 1.2.1
tables: 3.4.2
numexpr: 2.6.4
feather: None
matplotlib: 2.1.2
openpyxl: 2.4.10
xlrd: 1.1.0
xlwt: 1.3.0
xlsxwriter: 1.0.2
lxml: 4.2.1
bs4: 4.6.0
html5lib: 1.0.1
sqlalchemy: 1.2.1
pymysql: None
psycopg2: None
jinja2: 2.10
s3fs: 0.1.5
fastparquet: None
pandas_gbq: None
pandas_datareader: None
Here is a piece of code that replicates the issue:
```python
df_test = pd.DataFrame([["Hello", "World"], ["Foo", "Bar"]], columns=["Unnamed:_5", "Unnamed:_6"])
df_test[df_test['Unnamed:_6'] == "Bar"]  # succeeds
df_test.eval('Unnamed:_6 == "Bar"')  # exception
```

This is the full stacktrace:
```python-traceback
---------------------------------------------------------------------------
NotImplementedError                       Traceback (most recent call last)
<ipython-input-17-6f14251164a5> in <module>()
      1 df_test = pd.DataFrame([["Hello", "World"], ["Foo", "Bar"]], columns=["Unnamed:_5", "Unnamed:_6"])
      2 df_test[df_test['Unnamed:_6'] == "Bar"]
----> 3 df_test.eval('Unnamed:_6 == "Bar"')

~\AppData\Local\Continuum\anaconda3\lib\site-packages\pandas\core\frame.py in eval(self, expr, inplace, **kwargs)
   2960             kwargs['target'] = self
   2961         kwargs['resolvers'] = kwargs.get('resolvers', ()) + tuple(resolvers)
-> 2962         return _eval(expr, inplace=inplace, **kwargs)
   2963 
   2964     def select_dtypes(self, include=None, exclude=None):

~\AppData\Local\Continuum\anaconda3\lib\site-packages\pandas\core\computation\eval.py in eval(expr, parser, engine, truediv, local_dict, global_dict, resolvers, level, target, inplace)
    289 
    290         parsed_expr = Expr(expr, engine=engine, parser=parser, env=env,
--> 291                            truediv=truediv)
    292 
    293         # construct the engine and evaluate the parsed expression

~\AppData\Local\Continuum\anaconda3\lib\site-packages\pandas\core\computation\expr.py in __init__(self, expr, engine, parser, env, truediv, level)
    737         self.env.scope['truediv'] = truediv
    738         self._visitor = _parsers[parser](self.env, self.engine, self.parser)
--> 739         self.terms = self.parse()
    740 
    741     @property

~\AppData\Local\Continuum\anaconda3\lib\site-packages\pandas\core\computation\expr.py in parse(self)
    754     def parse(self):
    755         """Parse an expression"""
--> 756         return self._visitor.visit(self.expr)
    757 
    758     @property

~\AppData\Local\Continuum\anaconda3\lib\site-packages\pandas\core\computation\expr.py in visit(self, node, **kwargs)
    319         method = 'visit_' + node.__class__.__name__
    320         visitor = getattr(self, method)
--> 321         return visitor(node, **kwargs)
    322 
    323     def visit_Module(self, node, **kwargs):

~\AppData\Local\Continuum\anaconda3\lib\site-packages\pandas\core\computation\expr.py in visit_Module(self, node, **kwargs)
    325             raise SyntaxError('only a single expression is allowed')
    326         expr = node.body[0]
--> 327         return self.visit(expr, **kwargs)
    328 
    329     def visit_Expr(self, node, **kwargs):

~\AppData\Local\Continuum\anaconda3\lib\site-packages\pandas\core\computation\expr.py in visit(self, node, **kwargs)
    319         method = 'visit_' + node.__class__.__name__
    320         visitor = getattr(self, method)
--> 321         return visitor(node, **kwargs)
    322 
    323     def visit_Module(self, node, **kwargs):

~\AppData\Local\Continuum\anaconda3\lib\site-packages\pandas\core\computation\expr.py in f(self, *args, **kwargs)
    202     def f(self, *args, **kwargs):
    203         raise NotImplementedError("{name!r} nodes are not "
--> 204                                   "implemented".format(name=node_name))
    205     return f
    206 

NotImplementedError: 'AnnAssign' nodes are not implemented
```
@simtsc @WillAyd I tried recreating this in`master` and still got the error I mentioned earlier. Shall I open another issue for that?
@uds5501 Since your reported issue is different from mine I would suggest to open another ticket for it and continue the discussion there.
**Update**: I was experimenting a little with the column names and found that the characters used in the name have a profound impact on whether the call succeeds. I was also able to replicate @uds5501 issue as well. Here are my findings. I hope this helps to synchronize the behavior of the numpy-like API and eval/query API.
```python
df_test = pd.DataFrame([["Hello", "World"], ["Foo", "Bar"]], columns=['A:_', 'B:_'])
df_test[df_test['B:_'] == "Bar"]  # succeeds
df_test.query('B:_ == "Bar"')  # NotImplementedError: 'AnnAssign' nodes are not implemented
```

```python
df_test = pd.DataFrame([["Hello", "World"], ["Foo", "Bar"]], columns=['A:', 'B:'])
df_test[df_test['B:'] == "Bar"]  # succeeds
df_test.query('B: == "Bar"')  # SyntaxError: invalid syntax
```

```python
df_test = pd.DataFrame([["Hello", "World"], ["Foo", "Bar"]], columns=['A_', 'B_'])
df_test[df_test['B_'] == "Bar"]  # succeeds
df_test.query('B_ == "Bar"')  # succeeds
```

```python
df_test = pd.DataFrame([["Hello", "World"], ["Foo", "Bar"]], columns=list('AB'))
df_test[df_test['B'] == "Bar"]  # succeeds
df_test.query('B == "Bar"')  # succeeds
```

Resolving patch:
diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py
--- a/pandas/io/formats/style.py
+++ b/pandas/io/formats/style.py
@@ -30,6 +30,8 @@
 import pandas.core.common as com
 from pandas.core.indexing import _maybe_numeric_slice, _non_reducing_slice
 from pandas.util._decorators import Appender
+from pandas.core.dtypes.generic import ABCSeries
+
 try:
     import matplotlib.pyplot as plt
     from matplotlib import colors
@@ -993,174 +995,124 @@ def set_properties(self, subset=None, **kwargs):
         return self.applymap(f, subset=subset)
 
     @staticmethod
-    def _bar_left(s, color, width, base):
-        """
-        The minimum value is aligned at the left of the cell
-        Parameters
-        ----------
-        color: 2-tuple/list, of [``color_negative``, ``color_positive``]
-        width: float
-            A number between 0 or 100. The largest value will cover ``width``
-            percent of the cell's width
-        base: str
-            The base css format of the cell, e.g.:
-            ``base = 'width: 10em; height: 80%;'``
-        Returns
-        -------
-        self : Styler
-        """
-        normed = width * (s - s.min()) / (s.max() - s.min())
-        zero_normed = width * (0 - s.min()) / (s.max() - s.min())
-        attrs = (base + 'background: linear-gradient(90deg,{c} {w:.1f}%, '
-                        'transparent 0%)')
-
-        return [base if x == 0 else attrs.format(c=color[0], w=x)
-                if x < zero_normed
-                else attrs.format(c=color[1], w=x) if x >= zero_normed
-                else base for x in normed]
-
-    @staticmethod
-    def _bar_center_zero(s, color, width, base):
-        """
-        Creates a bar chart where the zero is centered in the cell
-        Parameters
-        ----------
-        color: 2-tuple/list, of [``color_negative``, ``color_positive``]
-        width: float
-            A number between 0 or 100. The largest value will cover ``width``
-            percent of the cell's width
-        base: str
-            The base css format of the cell, e.g.:
-            ``base = 'width: 10em; height: 80%;'``
-        Returns
-        -------
-        self : Styler
-        """
-
-        # Either the min or the max should reach the edge
-        # (50%, centered on zero)
-        m = max(abs(s.min()), abs(s.max()))
-
-        normed = s * 50 * width / (100.0 * m)
-
-        attrs_neg = (base + 'background: linear-gradient(90deg, transparent 0%'
-                     ', transparent {w:.1f}%, {c} {w:.1f}%, '
-                     '{c} 50%, transparent 50%)')
-
-        attrs_pos = (base + 'background: linear-gradient(90deg, transparent 0%'
-                     ', transparent 50%, {c} 50%, {c} {w:.1f}%, '
-                     'transparent {w:.1f}%)')
-
-        return [attrs_pos.format(c=color[1], w=(50 + x)) if x >= 0
-                else attrs_neg.format(c=color[0], w=(50 + x))
-                for x in normed]
+    def _bar(s, align, colors, width=100, vmin=None, vmax=None):
+        """Draw bar chart in dataframe cells"""
+
+        # Get input value range.
+        smin = s.min() if vmin is None else vmin
+        if isinstance(smin, ABCSeries):
+            smin = smin.min()
+        smax = s.max() if vmax is None else vmax
+        if isinstance(smax, ABCSeries):
+            smax = smax.max()
+        if align == 'mid':
+            smin = min(0, smin)
+            smax = max(0, smax)
+        elif align == 'zero':
+            # For "zero" mode, we want the range to be symmetrical around zero.
+            smax = max(abs(smin), abs(smax))
+            smin = -smax
+        # Transform to percent-range of linear-gradient
+        normed = width * (s.values - smin) / (smax - smin + 1e-12)
+        zero = -width * smin / (smax - smin + 1e-12)
+
+        def css_bar(start, end, color):
+            """Generate CSS code to draw a bar from start to end."""
+            css = 'width: 10em; height: 80%;'
+            if end > start:
+                css += 'background: linear-gradient(90deg,'
+                if start > 0:
+                    css += ' transparent {s:.1f}%, {c} {s:.1f}%, '.format(
+                        s=start, c=color
+                    )
+                css += '{c} {e:.1f}%, transparent {e:.1f}%)'.format(
+                    e=min(end, width), c=color,
+                )
+            return css
 
-    @staticmethod
-    def _bar_center_mid(s, color, width, base):
-        """
-        Creates a bar chart where the midpoint is centered in the cell
-        Parameters
-        ----------
-        color: 2-tuple/list, of [``color_negative``, ``color_positive``]
-        width: float
-            A number between 0 or 100. The largest value will cover ``width``
-            percent of the cell's width
-        base: str
-            The base css format of the cell, e.g.:
-            ``base = 'width: 10em; height: 80%;'``
-        Returns
-        -------
-        self : Styler
-        """
+        def css(x):
+            if pd.isna(x):
+                return ''
+            if align == 'left':
+                return css_bar(0, x, colors[x > zero])
+            else:
+                return css_bar(min(x, zero), max(x, zero), colors[x > zero])
 
-        if s.min() >= 0:
-            # In this case, we place the zero at the left, and the max() should
-            # be at width
-            zero = 0.0
-            slope = width / s.max()
-        elif s.max() <= 0:
-            # In this case, we place the zero at the right, and the min()
-            # should be at 100-width
-            zero = 100.0
-            slope = width / -s.min()
+        if s.ndim == 1:
+            return [css(x) for x in normed]
         else:
-            slope = width / (s.max() - s.min())
-            zero = (100.0 + width) / 2.0 - slope * s.max()
-
-        normed = zero + slope * s
-
-        attrs_neg = (base + 'background: linear-gradient(90deg, transparent 0%'
-                     ', transparent {w:.1f}%, {c} {w:.1f}%, '
-                     '{c} {zero:.1f}%, transparent {zero:.1f}%)')
-
-        attrs_pos = (base + 'background: linear-gradient(90deg, transparent 0%'
-                     ', transparent {zero:.1f}%, {c} {zero:.1f}%, '
-                     '{c} {w:.1f}%, transparent {w:.1f}%)')
-
-        return [attrs_pos.format(c=color[1], zero=zero, w=x) if x > zero
-                else attrs_neg.format(c=color[0], zero=zero, w=x)
-                for x in normed]
+            return pd.DataFrame(
+                [[css(x) for x in row] for row in normed],
+                index=s.index, columns=s.columns
+            )
 
     def bar(self, subset=None, axis=0, color='#d65f5f', width=100,
-            align='left'):
+            align='left', vmin=None, vmax=None):
         """
-        Color the background ``color`` proportional to the values in each
-        column.
-        Excludes non-numeric data by default.
+        Draw bar chart in the cell backgrounds.
 
         Parameters
         ----------
-        subset: IndexSlice, default None
-            a valid slice for ``data`` to limit the style application to
-        axis: int
-        color: str or 2-tuple/list
+        subset : IndexSlice, optional
+            A valid slice for `data` to limit the style application to.
+        axis : int, str or None, default 0
+            Apply to each column (`axis=0` or `'index'`)
+            or to each row (`axis=1` or `'columns'`) or
+            to the entire DataFrame at once with `axis=None`.
+        color : str or 2-tuple/list
             If a str is passed, the color is the same for both
             negative and positive numbers. If 2-tuple/list is used, the
             first element is the color_negative and the second is the
-            color_positive (eg: ['#d65f5f', '#5fba7d'])
-        width: float
-            A number between 0 or 100. The largest value will cover ``width``
-            percent of the cell's width
+            color_positive (eg: ['#d65f5f', '#5fba7d']).
+        width : float, default 100
+            A number between 0 or 100. The largest value will cover `width`
+            percent of the cell's width.
         align : {'left', 'zero',' mid'}, default 'left'
-            - 'left' : the min value starts at the left of the cell
-            - 'zero' : a value of zero is located at the center of the cell
+            How to align the bars with the cells.
+            - 'left' : the min value starts at the left of the cell.
+            - 'zero' : a value of zero is located at the center of the cell.
             - 'mid' : the center of the cell is at (max-min)/2, or
               if values are all negative (positive) the zero is aligned
-              at the right (left) of the cell
+              at the right (left) of the cell.
 
               .. versionadded:: 0.20.0
 
+        vmin : float, optional
+            Minimum bar value, defining the left hand limit
+            of the bar drawing range, lower values are clipped to `vmin`.
+            When None (default): the minimum value of the data will be used.
+
+            .. versionadded:: 0.24.0
+
+        vmax : float, optional
+            Maximum bar value, defining the right hand limit
+            of the bar drawing range, higher values are clipped to `vmax`.
+            When None (default): the maximum value of the data will be used.
+
+            .. versionadded:: 0.24.0
+
+
         Returns
         -------
         self : Styler
         """
-        subset = _maybe_numeric_slice(self.data, subset)
-        subset = _non_reducing_slice(subset)
+        if align not in ('left', 'zero', 'mid'):
+            raise ValueError("`align` must be one of {'left', 'zero',' mid'}")
 
-        base = 'width: 10em; height: 80%;'
-
-        if not(is_list_like(color)):
+        if not (is_list_like(color)):
             color = [color, color]
         elif len(color) == 1:
             color = [color[0], color[0]]
         elif len(color) > 2:
-            msg = ("Must pass `color` as string or a list-like"
-                   " of length 2: [`color_negative`, `color_positive`]\n"
-                   "(eg: color=['#d65f5f', '#5fba7d'])")
-            raise ValueError(msg)
+            raise ValueError("`color` must be string or a list-like"
+                             " of length 2: [`color_neg`, `color_pos`]"
+                             " (eg: color=['#d65f5f', '#5fba7d'])")
 
-        if align == 'left':
-            self.apply(self._bar_left, subset=subset, axis=axis, color=color,
-                       width=width, base=base)
-        elif align == 'zero':
-            self.apply(self._bar_center_zero, subset=subset, axis=axis,
-                       color=color, width=width, base=base)
-        elif align == 'mid':
-            self.apply(self._bar_center_mid, subset=subset, axis=axis,
-                       color=color, width=width, base=base)
-        else:
-            msg = ("`align` must be one of {'left', 'zero',' mid'}")
-            raise ValueError(msg)
+        subset = _maybe_numeric_slice(self.data, subset)
+        subset = _non_reducing_slice(subset)
+        self.apply(self._bar, subset=subset, axis=axis,
+                   align=align, colors=color, width=width,
+                   vmin=vmin, vmax=vmax)
 
         return self
 


Test patch:
diff --git a/pandas/tests/io/formats/test_style.py b/pandas/tests/io/formats/test_style.py
--- a/pandas/tests/io/formats/test_style.py
+++ b/pandas/tests/io/formats/test_style.py
@@ -349,10 +349,10 @@ def test_bar_align_left(self):
             (0, 0): ['width: 10em', ' height: 80%'],
             (1, 0): ['width: 10em', ' height: 80%',
                      'background: linear-gradient('
-                     '90deg,#d65f5f 50.0%, transparent 0%)'],
+                     '90deg,#d65f5f 50.0%, transparent 50.0%)'],
             (2, 0): ['width: 10em', ' height: 80%',
                      'background: linear-gradient('
-                     '90deg,#d65f5f 100.0%, transparent 0%)']
+                     '90deg,#d65f5f 100.0%, transparent 100.0%)']
         }
         assert result == expected
 
@@ -361,10 +361,10 @@ def test_bar_align_left(self):
             (0, 0): ['width: 10em', ' height: 80%'],
             (1, 0): ['width: 10em', ' height: 80%',
                      'background: linear-gradient('
-                     '90deg,red 25.0%, transparent 0%)'],
+                     '90deg,red 25.0%, transparent 25.0%)'],
             (2, 0): ['width: 10em', ' height: 80%',
                      'background: linear-gradient('
-                     '90deg,red 50.0%, transparent 0%)']
+                     '90deg,red 50.0%, transparent 50.0%)']
         }
         assert result == expected
 
@@ -383,46 +383,46 @@ def test_bar_align_left_0points(self):
                     (0, 2): ['width: 10em', ' height: 80%'],
                     (1, 0): ['width: 10em', ' height: 80%',
                              'background: linear-gradient(90deg,#d65f5f 50.0%,'
-                             ' transparent 0%)'],
+                             ' transparent 50.0%)'],
                     (1, 1): ['width: 10em', ' height: 80%',
                              'background: linear-gradient(90deg,#d65f5f 50.0%,'
-                             ' transparent 0%)'],
+                             ' transparent 50.0%)'],
                     (1, 2): ['width: 10em', ' height: 80%',
                              'background: linear-gradient(90deg,#d65f5f 50.0%,'
-                             ' transparent 0%)'],
+                             ' transparent 50.0%)'],
                     (2, 0): ['width: 10em', ' height: 80%',
                              'background: linear-gradient(90deg,#d65f5f 100.0%'
-                             ', transparent 0%)'],
+                             ', transparent 100.0%)'],
                     (2, 1): ['width: 10em', ' height: 80%',
                              'background: linear-gradient(90deg,#d65f5f 100.0%'
-                             ', transparent 0%)'],
+                             ', transparent 100.0%)'],
                     (2, 2): ['width: 10em', ' height: 80%',
                              'background: linear-gradient(90deg,#d65f5f 100.0%'
-                             ', transparent 0%)']}
+                             ', transparent 100.0%)']}
         assert result == expected
 
         result = df.style.bar(axis=1)._compute().ctx
         expected = {(0, 0): ['width: 10em', ' height: 80%'],
                     (0, 1): ['width: 10em', ' height: 80%',
                              'background: linear-gradient(90deg,#d65f5f 50.0%,'
-                             ' transparent 0%)'],
+                             ' transparent 50.0%)'],
                     (0, 2): ['width: 10em', ' height: 80%',
                              'background: linear-gradient(90deg,#d65f5f 100.0%'
-                             ', transparent 0%)'],
+                             ', transparent 100.0%)'],
                     (1, 0): ['width: 10em', ' height: 80%'],
                     (1, 1): ['width: 10em', ' height: 80%',
                              'background: linear-gradient(90deg,#d65f5f 50.0%'
-                             ', transparent 0%)'],
+                             ', transparent 50.0%)'],
                     (1, 2): ['width: 10em', ' height: 80%',
                              'background: linear-gradient(90deg,#d65f5f 100.0%'
-                             ', transparent 0%)'],
+                             ', transparent 100.0%)'],
                     (2, 0): ['width: 10em', ' height: 80%'],
                     (2, 1): ['width: 10em', ' height: 80%',
                              'background: linear-gradient(90deg,#d65f5f 50.0%'
-                             ', transparent 0%)'],
+                             ', transparent 50.0%)'],
                     (2, 2): ['width: 10em', ' height: 80%',
                              'background: linear-gradient(90deg,#d65f5f 100.0%'
-                             ', transparent 0%)']}
+                             ', transparent 100.0%)']}
         assert result == expected
 
     def test_bar_align_mid_pos_and_neg(self):
@@ -432,21 +432,16 @@ def test_bar_align_mid_pos_and_neg(self):
                               '#d65f5f', '#5fba7d'])._compute().ctx
 
         expected = {(0, 0): ['width: 10em', ' height: 80%',
-                             'background: linear-gradient(90deg, '
-                             'transparent 0%, transparent 0.0%, #d65f5f 0.0%, '
+                             'background: linear-gradient(90deg,'
                              '#d65f5f 10.0%, transparent 10.0%)'],
-                    (1, 0): ['width: 10em', ' height: 80%',
-                             'background: linear-gradient(90deg, '
-                             'transparent 0%, transparent 10.0%, '
-                             '#d65f5f 10.0%, #d65f5f 10.0%, '
-                             'transparent 10.0%)'],
+                    (1, 0): ['width: 10em', ' height: 80%', ],
                     (2, 0): ['width: 10em', ' height: 80%',
                              'background: linear-gradient(90deg, '
-                             'transparent 0%, transparent 10.0%, #5fba7d 10.0%'
+                             'transparent 10.0%, #5fba7d 10.0%'
                              ', #5fba7d 30.0%, transparent 30.0%)'],
                     (3, 0): ['width: 10em', ' height: 80%',
                              'background: linear-gradient(90deg, '
-                             'transparent 0%, transparent 10.0%, '
+                             'transparent 10.0%, '
                              '#5fba7d 10.0%, #5fba7d 100.0%, '
                              'transparent 100.0%)']}
 
@@ -459,20 +454,16 @@ def test_bar_align_mid_all_pos(self):
                               '#d65f5f', '#5fba7d'])._compute().ctx
 
         expected = {(0, 0): ['width: 10em', ' height: 80%',
-                             'background: linear-gradient(90deg, '
-                             'transparent 0%, transparent 0.0%, #5fba7d 0.0%, '
+                             'background: linear-gradient(90deg,'
                              '#5fba7d 10.0%, transparent 10.0%)'],
                     (1, 0): ['width: 10em', ' height: 80%',
-                             'background: linear-gradient(90deg, '
-                             'transparent 0%, transparent 0.0%, #5fba7d 0.0%, '
+                             'background: linear-gradient(90deg,'
                              '#5fba7d 20.0%, transparent 20.0%)'],
                     (2, 0): ['width: 10em', ' height: 80%',
-                             'background: linear-gradient(90deg, '
-                             'transparent 0%, transparent 0.0%, #5fba7d 0.0%, '
+                             'background: linear-gradient(90deg,'
                              '#5fba7d 50.0%, transparent 50.0%)'],
                     (3, 0): ['width: 10em', ' height: 80%',
-                             'background: linear-gradient(90deg, '
-                             'transparent 0%, transparent 0.0%, #5fba7d 0.0%, '
+                             'background: linear-gradient(90deg,'
                              '#5fba7d 100.0%, transparent 100.0%)']}
 
         assert result == expected
@@ -484,23 +475,21 @@ def test_bar_align_mid_all_neg(self):
                               '#d65f5f', '#5fba7d'])._compute().ctx
 
         expected = {(0, 0): ['width: 10em', ' height: 80%',
-                             'background: linear-gradient(90deg, '
-                             'transparent 0%, transparent 0.0%, '
-                             '#d65f5f 0.0%, #d65f5f 100.0%, '
-                             'transparent 100.0%)'],
+                             'background: linear-gradient(90deg,'
+                             '#d65f5f 100.0%, transparent 100.0%)'],
                     (1, 0): ['width: 10em', ' height: 80%',
                              'background: linear-gradient(90deg, '
-                             'transparent 0%, transparent 40.0%, '
+                             'transparent 40.0%, '
                              '#d65f5f 40.0%, #d65f5f 100.0%, '
                              'transparent 100.0%)'],
                     (2, 0): ['width: 10em', ' height: 80%',
                              'background: linear-gradient(90deg, '
-                             'transparent 0%, transparent 70.0%, '
+                             'transparent 70.0%, '
                              '#d65f5f 70.0%, #d65f5f 100.0%, '
                              'transparent 100.0%)'],
                     (3, 0): ['width: 10em', ' height: 80%',
                              'background: linear-gradient(90deg, '
-                             'transparent 0%, transparent 80.0%, '
+                             'transparent 80.0%, '
                              '#d65f5f 80.0%, #d65f5f 100.0%, '
                              'transparent 100.0%)']}
         assert result == expected
@@ -511,25 +500,194 @@ def test_bar_align_zero_pos_and_neg(self):
 
         result = df.style.bar(align='zero', color=[
                               '#d65f5f', '#5fba7d'], width=90)._compute().ctx
-
         expected = {(0, 0): ['width: 10em', ' height: 80%',
                              'background: linear-gradient(90deg, '
-                             'transparent 0%, transparent 45.0%, '
-                             '#d65f5f 45.0%, #d65f5f 50%, '
-                             'transparent 50%)'],
-                    (1, 0): ['width: 10em', ' height: 80%',
-                             'background: linear-gradient(90deg, '
-                             'transparent 0%, transparent 50%, '
-                             '#5fba7d 50%, #5fba7d 50.0%, '
-                             'transparent 50.0%)'],
+                             'transparent 40.0%, #d65f5f 40.0%, '
+                             '#d65f5f 45.0%, transparent 45.0%)'],
+                    (1, 0): ['width: 10em', ' height: 80%'],
                     (2, 0): ['width: 10em', ' height: 80%',
                              'background: linear-gradient(90deg, '
-                             'transparent 0%, transparent 50%, #5fba7d 50%, '
-                             '#5fba7d 60.0%, transparent 60.0%)'],
+                             'transparent 45.0%, #5fba7d 45.0%, '
+                             '#5fba7d 55.0%, transparent 55.0%)'],
                     (3, 0): ['width: 10em', ' height: 80%',
                              'background: linear-gradient(90deg, '
-                             'transparent 0%, transparent 50%, #5fba7d 50%, '
-                             '#5fba7d 95.0%, transparent 95.0%)']}
+                             'transparent 45.0%, #5fba7d 45.0%, '
+                             '#5fba7d 90.0%, transparent 90.0%)']}
+        assert result == expected
+
+    def test_bar_align_left_axis_none(self):
+        df = pd.DataFrame({'A': [0, 1], 'B': [2, 4]})
+        result = df.style.bar(axis=None)._compute().ctx
+        expected = {
+            (0, 0): ['width: 10em', ' height: 80%'],
+            (1, 0): ['width: 10em', ' height: 80%',
+                     'background: linear-gradient(90deg,'
+                     '#d65f5f 25.0%, transparent 25.0%)'],
+            (0, 1): ['width: 10em', ' height: 80%',
+                     'background: linear-gradient(90deg,'
+                     '#d65f5f 50.0%, transparent 50.0%)'],
+            (1, 1): ['width: 10em', ' height: 80%',
+                     'background: linear-gradient(90deg,'
+                     '#d65f5f 100.0%, transparent 100.0%)']
+        }
+        assert result == expected
+
+    def test_bar_align_zero_axis_none(self):
+        df = pd.DataFrame({'A': [0, 1], 'B': [-2, 4]})
+        result = df.style.bar(align='zero', axis=None)._compute().ctx
+        expected = {
+            (0, 0): ['width: 10em', ' height: 80%'],
+            (1, 0): ['width: 10em', ' height: 80%',
+                     'background: linear-gradient(90deg, '
+                     'transparent 50.0%, #d65f5f 50.0%, '
+                     '#d65f5f 62.5%, transparent 62.5%)'],
+            (0, 1): ['width: 10em', ' height: 80%',
+                     'background: linear-gradient(90deg, '
+                     'transparent 25.0%, #d65f5f 25.0%, '
+                     '#d65f5f 50.0%, transparent 50.0%)'],
+            (1, 1): ['width: 10em', ' height: 80%',
+                     'background: linear-gradient(90deg, '
+                     'transparent 50.0%, #d65f5f 50.0%, '
+                     '#d65f5f 100.0%, transparent 100.0%)']
+        }
+        assert result == expected
+
+    def test_bar_align_mid_axis_none(self):
+        df = pd.DataFrame({'A': [0, 1], 'B': [-2, 4]})
+        result = df.style.bar(align='mid', axis=None)._compute().ctx
+        expected = {
+            (0, 0): ['width: 10em', ' height: 80%'],
+            (1, 0): ['width: 10em', ' height: 80%',
+                     'background: linear-gradient(90deg, '
+                     'transparent 33.3%, #d65f5f 33.3%, '
+                     '#d65f5f 50.0%, transparent 50.0%)'],
+            (0, 1): ['width: 10em', ' height: 80%',
+                     'background: linear-gradient(90deg,'
+                     '#d65f5f 33.3%, transparent 33.3%)'],
+            (1, 1): ['width: 10em', ' height: 80%',
+                     'background: linear-gradient(90deg, '
+                     'transparent 33.3%, #d65f5f 33.3%, '
+                     '#d65f5f 100.0%, transparent 100.0%)']
+        }
+        assert result == expected
+
+    def test_bar_align_mid_vmin(self):
+        df = pd.DataFrame({'A': [0, 1], 'B': [-2, 4]})
+        result = df.style.bar(align='mid', axis=None, vmin=-6)._compute().ctx
+        expected = {
+            (0, 0): ['width: 10em', ' height: 80%'],
+            (1, 0): ['width: 10em', ' height: 80%',
+                     'background: linear-gradient(90deg, '
+                     'transparent 60.0%, #d65f5f 60.0%, '
+                     '#d65f5f 70.0%, transparent 70.0%)'],
+            (0, 1): ['width: 10em', ' height: 80%',
+                     'background: linear-gradient(90deg, '
+                     'transparent 40.0%, #d65f5f 40.0%, '
+                     '#d65f5f 60.0%, transparent 60.0%)'],
+            (1, 1): ['width: 10em', ' height: 80%',
+                     'background: linear-gradient(90deg, '
+                     'transparent 60.0%, #d65f5f 60.0%, '
+                     '#d65f5f 100.0%, transparent 100.0%)']
+        }
+        assert result == expected
+
+    def test_bar_align_mid_vmax(self):
+        df = pd.DataFrame({'A': [0, 1], 'B': [-2, 4]})
+        result = df.style.bar(align='mid', axis=None, vmax=8)._compute().ctx
+        expected = {
+            (0, 0): ['width: 10em', ' height: 80%'],
+            (1, 0): ['width: 10em', ' height: 80%',
+                     'background: linear-gradient(90deg, '
+                     'transparent 20.0%, #d65f5f 20.0%, '
+                     '#d65f5f 30.0%, transparent 30.0%)'],
+            (0, 1): ['width: 10em', ' height: 80%',
+                     'background: linear-gradient(90deg,'
+                     '#d65f5f 20.0%, transparent 20.0%)'],
+            (1, 1): ['width: 10em', ' height: 80%',
+                     'background: linear-gradient(90deg, '
+                     'transparent 20.0%, #d65f5f 20.0%, '
+                     '#d65f5f 60.0%, transparent 60.0%)']
+        }
+        assert result == expected
+
+    def test_bar_align_mid_vmin_vmax_wide(self):
+        df = pd.DataFrame({'A': [0, 1], 'B': [-2, 4]})
+        result = df.style.bar(align='mid', axis=None,
+                              vmin=-3, vmax=7)._compute().ctx
+        expected = {
+            (0, 0): ['width: 10em', ' height: 80%'],
+            (1, 0): ['width: 10em', ' height: 80%',
+                     'background: linear-gradient(90deg, '
+                     'transparent 30.0%, #d65f5f 30.0%, '
+                     '#d65f5f 40.0%, transparent 40.0%)'],
+            (0, 1): ['width: 10em', ' height: 80%',
+                     'background: linear-gradient(90deg, '
+                     'transparent 10.0%, #d65f5f 10.0%, '
+                     '#d65f5f 30.0%, transparent 30.0%)'],
+            (1, 1): ['width: 10em', ' height: 80%',
+                     'background: linear-gradient(90deg, '
+                     'transparent 30.0%, #d65f5f 30.0%, '
+                     '#d65f5f 70.0%, transparent 70.0%)']
+        }
+        assert result == expected
+
+    def test_bar_align_mid_vmin_vmax_clipping(self):
+        df = pd.DataFrame({'A': [0, 1], 'B': [-2, 4]})
+        result = df.style.bar(align='mid', axis=None,
+                              vmin=-1, vmax=3)._compute().ctx
+        expected = {
+            (0, 0): ['width: 10em', ' height: 80%'],
+            (1, 0): ['width: 10em', ' height: 80%',
+                     'background: linear-gradient(90deg, '
+                     'transparent 25.0%, #d65f5f 25.0%, '
+                     '#d65f5f 50.0%, transparent 50.0%)'],
+            (0, 1): ['width: 10em', ' height: 80%',
+                     'background: linear-gradient(90deg,'
+                     '#d65f5f 25.0%, transparent 25.0%)'],
+            (1, 1): ['width: 10em', ' height: 80%',
+                     'background: linear-gradient(90deg, '
+                     'transparent 25.0%, #d65f5f 25.0%, '
+                     '#d65f5f 100.0%, transparent 100.0%)']
+        }
+        assert result == expected
+
+    def test_bar_align_mid_nans(self):
+        df = pd.DataFrame({'A': [1, None], 'B': [-1, 3]})
+        result = df.style.bar(align='mid', axis=None)._compute().ctx
+        expected = {
+            (0, 0): ['width: 10em', ' height: 80%',
+                     'background: linear-gradient(90deg, '
+                     'transparent 25.0%, #d65f5f 25.0%, '
+                     '#d65f5f 50.0%, transparent 50.0%)'],
+            (1, 0): [''],
+            (0, 1): ['width: 10em', ' height: 80%',
+                     'background: linear-gradient(90deg,'
+                     '#d65f5f 25.0%, transparent 25.0%)'],
+            (1, 1): ['width: 10em', ' height: 80%',
+                     'background: linear-gradient(90deg, '
+                     'transparent 25.0%, #d65f5f 25.0%, '
+                     '#d65f5f 100.0%, transparent 100.0%)']
+        }
+        assert result == expected
+
+    def test_bar_align_zero_nans(self):
+        df = pd.DataFrame({'A': [1, None], 'B': [-1, 2]})
+        result = df.style.bar(align='zero', axis=None)._compute().ctx
+        expected = {
+            (0, 0): ['width: 10em', ' height: 80%',
+                     'background: linear-gradient(90deg, '
+                     'transparent 50.0%, #d65f5f 50.0%, '
+                     '#d65f5f 75.0%, transparent 75.0%)'],
+            (1, 0): [''],
+            (0, 1): ['width: 10em', ' height: 80%',
+                     'background: linear-gradient(90deg, '
+                     'transparent 25.0%, #d65f5f 25.0%, '
+                     '#d65f5f 50.0%, transparent 50.0%)'],
+            (1, 1): ['width: 10em', ' height: 80%',
+                     'background: linear-gradient(90deg, '
+                     'transparent 50.0%, #d65f5f 50.0%, '
+                     '#d65f5f 100.0%, transparent 100.0%)']
+        }
         assert result == expected
 
     def test_bar_bad_align_raises(self):