Repository: vaexio/vaex

Issue:
[BUG-REPORT] ValueError: operand '!=' not supported for string comparison
**Description**
When comparing two string columns, the `==` operator works, and the `!=` fails.


**Software information**
version: 
```{'vaex': '4.1.0',
 'vaex-core': '4.1.0',
 'vaex-viz': '0.5.0',
 'vaex-hdf5': '0.7.0',
 'vaex-server': '0.4.0',
 'vaex-astro': '0.8.0',
 'vaex-jupyter': '0.6.0',
 'vaex-ml': '0.11.1'}
``` 

 - Vaex was installed via: pip
 - OS: OSX

**Additional information**
Reproduce: 

```
import vaex
df = vaex.from_arrays(x=['a','b'],y=['a','a'])
df['x']==df['y'] # this is ok
df['x']!=df['y'] # this is not ok

ValueError                                Traceback (most recent call last)
<ipython-input-173-90a5ac16bede> in <module>
      1 df = vaex.from_arrays(x=['a','b'],y=['a','a'])
----> 2 df['x']!=df['y']

~/.virtualenvs/baloo/lib/python3.7/site-packages/vaex/expression.py in f(a, b)
    110                             expression = 'str_cat({0}, {1})'.format(a.expression, b)
    111                         else:
--> 112                             raise ValueError('operand %r not supported for string comparison' % op['code'])
    113                         return Expression(self.ds, expression=expression)
    114                     else:

ValueError: operand '!=' not supported for string comparison
```



Discussion:
Possibly related? These two return the wrong results, but possible it's doing object comparison instead of value comparison.

![image](https://user-images.githubusercontent.com/20652336/116841809-25560700-ab8f-11eb-93cd-5e73955af976.png)

Resolving patch:
diff --git a/packages/vaex-core/vaex/expression.py b/packages/vaex-core/vaex/expression.py
--- a/packages/vaex-core/vaex/expression.py
+++ b/packages/vaex-core/vaex/expression.py
@@ -106,6 +106,8 @@ def f(a, b):
                             b = repr(b)
                         if op['code'] == '==':
                             expression = 'str_equals({0}, {1})'.format(a.expression, b)
+                        elif op['code'] == '!=':
+                            expression = 'str_notequals({0}, {1})'.format(a.expression, b)
                         elif op['code'] == '+':
                             expression = 'str_cat({0}, {1})'.format(a.expression, b)
                         else:
diff --git a/packages/vaex-core/vaex/functions.py b/packages/vaex-core/vaex/functions.py
--- a/packages/vaex-core/vaex/functions.py
+++ b/packages/vaex-core/vaex/functions.py
@@ -1036,6 +1036,49 @@ def str_equals(x, y):
     return equals_mask
 
 
+@register_function(scope='str')
+@auto_str_unwrap
+def str_notequals(x, y):
+    """Tests if strings x and y are the not same
+
+    :returns: a boolean expression
+
+    Example:
+
+    >>> import vaex
+    >>> text = ['Something', 'very pretty', 'is coming', 'our', 'way.']
+    >>> df = vaex.from_arrays(text=text)
+    >>> df
+      #  text
+      0  Something
+      1  very pretty
+      2  is coming
+      3  our
+      4  way.
+
+    >>> df.text.str.notequals(df.text)
+    Expression = str_notequals(text, text)
+    Length: 5 dtype: bool (expression)
+    ----------------------------------
+    0  False
+    1  False
+    2  False
+    3  False
+    4  False
+
+    >>> df.text.str.notequals('our')
+    Expression = str_notequals(text, 'our')
+    Length: 5 dtype: bool (expression)
+    ----------------------------------
+    0   True
+    1   True
+    2   True
+    3  False
+    4   True
+    """
+    return ~str_equals(x, y)
+
+
 @register_function(scope='str')
 @auto_str_unwrap
 def str_capitalize(x):


Test patch:
diff --git a/tests/strings_test.py b/tests/strings_test.py
--- a/tests/strings_test.py
+++ b/tests/strings_test.py
@@ -437,6 +437,14 @@ def test_strings_operator_equals(dfs, match):
     assert (dfs.s == dfs.s).tolist() == [k == k for k in string_list]
 
 
+@pytest.mark.skipif(sys.version_info[0] == 2, reason="no support for python2")
+@pytest.mark.parametrize("match", ["vaex", "VæX! "])
+def test_strings_operator_notequals(dfs, match):
+    assert (dfs.s != match).tolist() == [k != match for k in string_list]
+    assert (match != dfs.s).tolist() == [k != match for k in string_list]
+    assert (dfs.s != dfs.s).tolist() == [k != k for k in string_list]
+
+
 @pytest.mark.skipif(sys.version_info[0] == 2, reason="no support for python2")
 @pytest.mark.parametrize("extra", ["vaex", "VæX! "])
 def test_strings_operator_plus(dfs, extra):