nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: altair-viz/altair
Pull request #1107 (2018-08-21 15:23:09)

Appropriately handle non-string column names
Replaces #899

jakevdp: @ellisonbg let me know if this looks OK

Patch:
diff --git a/CHANGES.md b/CHANGES.md
index be8148ea..7269eece 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -4,6 +4,9 @@
 
 ### Enhancements
 
+- Better errors for non-string column names, as well as automatic conversion
+  of ``pandas.RangeIndex`` columns to strings (#1107)
+
 ### Backward-incompatible changes
 
 ### Maintenance
diff --git a/altair/utils/core.py b/altair/utils/core.py
index 6f49e4fe..65fa508d 100644
--- a/altair/utils/core.py
+++ b/altair/utils/core.py
@@ -91,6 +91,8 @@ def sanitize_dataframe(df):
     """Sanitize a DataFrame to prepare it for serialization.
 
     * Make a copy
+    * Convert RangeIndex columns to strings
+    * Raise ValueError if column names are not strings
     * Raise ValueError if it has a hierarchical index.
     * Convert categoricals to strings.
     * Convert np.bool_ dtypes to Python bool objects
@@ -101,9 +103,17 @@ def sanitize_dataframe(df):
     """
     df = df.copy()
 
-    if isinstance(df.index, pd.core.index.MultiIndex):
+    if isinstance(df.columns, pd.RangeIndex):
+        df.columns = df.columns.astype(str)
+
+    for col in df.columns:
+        if not isinstance(col, six.string_types):
+            raise ValueError('Dataframe contains invalid column name: {0!r}. '
+                             'Column names must be strings'.format(col))
+
+    if isinstance(df.index, pd.MultiIndex):
         raise ValueError('Hierarchical indices not supported')
-    if isinstance(df.columns, pd.core.index.MultiIndex):
+    if isinstance(df.columns, pd.MultiIndex):
         raise ValueError('Hierarchical indices not supported')
 
     def to_list_if_array(val):


Test patch:
diff --git a/altair/utils/tests/test_utils.py b/altair/utils/tests/test_utils.py
index bd1245a5..8185ad2d 100644
--- a/altair/utils/tests/test_utils.py
+++ b/altair/utils/tests/test_utils.py
@@ -4,6 +4,7 @@ import json
 
 import numpy as np
 import pandas as pd
+import six
 
 from .. import infer_vegalite_type, sanitize_dataframe
 
@@ -80,6 +81,20 @@ def test_sanitize_dataframe():
     assert df.equals(df2)
 
 
+def test_sanitize_dataframe_colnames():
+    df = pd.DataFrame(np.arange(12).reshape(4, 3))
+
+    # Test that RangeIndex is converted to strings
+    df = sanitize_dataframe(df)
+    assert [isinstance(col, six.string_types) for col in df.columns]
+
+    # Test that non-string columns result in an error
+    df.columns = [4, 'foo', 'bar']
+    with pytest.raises(ValueError) as err:
+        sanitize_dataframe(df)
+    assert str(err.value).startswith('Dataframe contains invalid column name: 4.')
+
+
 def test_sanitize_dataframe_timedelta():
     df = pd.DataFrame({'r': pd.timedelta_range(start='1 day', periods=4)})
     with pytest.raises(ValueError) as err:
Repository: altair-viz/altair
Pull request #1384 (2019-03-23 15:49:02)

BUG: fix parsing of 'count()' as window op


Patch:
diff --git a/altair/utils/core.py b/altair/utils/core.py
index c151ee2f..e3258483 100644
--- a/altair/utils/core.py
+++ b/altair/utils/core.py
@@ -249,18 +249,19 @@ def parse_shorthand(shorthand, data=None, parse_aggregates=True,
 
     units = dict(field='(?P<field>.*)',
                  type='(?P<type>{})'.format('|'.join(valid_typecodes)),
-                 count='(?P<aggregate>count)',
+                 agg_count='(?P<aggregate>count)',
+                 op_count='(?P<op>count)',
                  aggregate='(?P<aggregate>{})'.format('|'.join(AGGREGATES)),
                  window_op='(?P<op>{})'.format('|'.join(AGGREGATES + WINDOW_AGGREGATES)),
                  timeUnit='(?P<timeUnit>{})'.format('|'.join(TIMEUNITS)))
 
     patterns = []
 
-    if parse_aggregates or parse_window_ops:
-        patterns.extend([r'{count}\(\)'])
     if parse_aggregates:
+        patterns.extend([r'{agg_count}\(\)'])
         patterns.extend([r'{aggregate}\({field}\)'])
     if parse_window_ops:
+        patterns.extend([r'{op_count}\(\)'])
         patterns.extend([r'{window_op}\({field}\)'])
     if parse_timeunits:
         patterns.extend([r'{timeUnit}\({field}\)'])


Test patch:
diff --git a/altair/utils/tests/test_core.py b/altair/utils/tests/test_core.py
index 1bd8f2ef..0f168a9f 100644
--- a/altair/utils/tests/test_core.py
+++ b/altair/utils/tests/test_core.py
@@ -92,6 +92,14 @@ def test_parse_shorthand_all_timeunits():
                                               'type': 'quantitative'}
 
 
+def test_parse_shorthand_window_count():
+    shorthand = "count()"
+    dct = parse_shorthand(shorthand,
+                          parse_aggregates=False, parse_window_ops=True, 
+                          parse_timeunits=False, parse_types=False)
+    assert dct == {'op': 'count'}
+
+
 def test_parse_shorthand_all_window_ops():
     window_ops = alt.Root._schema['definitions']['WindowOnlyOp']['enum']
     aggregates = alt.Root._schema['definitions']['AggregateOp']['enum']
Repository: altair-viz/altair
Pull request #1436 (2019-04-17 19:47:25)

vegalite 3 fixes
Fixes for a few issues encountered while trying to build the docs.


Patch:
diff --git a/altair/sphinxext/altairplot.py b/altair/sphinxext/altairplot.py
index ea489ae7..8c0c1600 100644
--- a/altair/sphinxext/altairplot.py
+++ b/altair/sphinxext/altairplot.py
@@ -241,7 +241,10 @@ def html_visit_altair_plot(self, node):
     elif output == 'plot':
         if isinstance(chart, alt.TopLevelMixin):
             # Last line should be a chart; convert to spec dict
-            spec = chart.to_dict()
+            try:
+                spec = chart.to_dict()
+            except alt.utils.schemapi.SchemaValidationError:
+                raise ValueError("Invalid chart: {0}".format(node['code']))
             actions = node['links']
 
             # TODO: add an option to save spects to file & load from there.
diff --git a/altair/vegalite/v3/api.py b/altair/vegalite/v3/api.py
index 58039645..6da47a39 100644
--- a/altair/vegalite/v3/api.py
+++ b/altair/vegalite/v3/api.py
@@ -509,7 +509,11 @@ class TopLevelMixin(mixins.ConfigMethodMixin):
         """
         copy = self.copy(deep=True, ignore=['data'])
         for key, val in kwargs.items():
-            setattr(copy, key, val)
+            if key == 'selection' and isinstance(val, Selection):
+                # For backward compatibility with old selection interface.
+                setattr(copy, key, {val.name: val.selection})
+            else:
+                setattr(copy, key, val)
         return copy
 
     def add_selection(self, *selections):


Test patch:
diff --git a/altair/vegalite/v3/tests/test_api.py b/altair/vegalite/v3/tests/test_api.py
index 49c803af..3d1e8808 100644
--- a/altair/vegalite/v3/tests/test_api.py
+++ b/altair/vegalite/v3/tests/test_api.py
@@ -414,6 +414,15 @@ def test_add_selection():
     assert chart.selection == expected
 
 
+def test_selection_property():
+    sel = alt.selection_interval()
+    chart = alt.Chart('data.csv').mark_point().properties(
+        selection=sel
+    )
+
+    assert list(chart['selection'].keys()) == [sel.name]
+
+
 def test_LookupData():
     df = pd.DataFrame({'x': [1, 2, 3], 'y': [4, 5, 6]})
     lookup = alt.LookupData(data=df, key='x')
Repository: altair-viz/altair
Pull request #1454 (2019-04-24 03:44:30)

BUG: fix Selection counter issue
The selection counter was not being properly incremented, breaking charts with multiple selectors.


Patch:
diff --git a/altair/vegalite/v3/api.py b/altair/vegalite/v3/api.py
index d98d6473..2d9c71c5 100644
--- a/altair/vegalite/v3/api.py
+++ b/altair/vegalite/v3/api.py
@@ -157,10 +157,13 @@ class Selection(object):
     def __init__(self, name, selection):
         if name is None:
             name = "selector{:03d}".format(self._counter)
-            self._counter += 1
+            self.__class__._counter += 1
         self.name = name
         self.selection = selection
 
+    def __repr__(self):
+        return "Selection({0!r}, {1})".format(self.name, self.selection)
+
     def ref(self):
         return {'selection': self.name}
 


Test patch:
diff --git a/altair/vegalite/v3/tests/test_api.py b/altair/vegalite/v3/tests/test_api.py
index 3d1e8808..1a2c7d30 100644
--- a/altair/vegalite/v3/tests/test_api.py
+++ b/altair/vegalite/v3/tests/test_api.py
@@ -278,6 +278,13 @@ def test_selection():
     assert isinstance(single | multi, alt.SelectionOr)
     assert isinstance(~single, alt.SelectionNot)
 
+    # test that default names increment (regression for #1454)
+    sel1 = alt.selection_single()
+    sel2 = alt.selection_multi()
+    sel3 = alt.selection_interval()
+    names = {s.name for s in (sel1, sel2, sel3)}
+    assert len(names) == 3
+
 
 def test_transforms():
     # aggregate transform
Repository: altair-viz/altair
Pull request #1497 (2019-05-09 19:51:15)

BUG: fix wrapped facet implementation


Patch:
diff --git a/altair/vegalite/v3/api.py b/altair/vegalite/v3/api.py
index a57e009c..22cd722d 100644
--- a/altair/vegalite/v3/api.py
+++ b/altair/vegalite/v3/api.py
@@ -1494,9 +1494,8 @@ class EncodingMixin(object):
             data, self.data = self.data, Undefined
 
         if facet_specified:
-            if isinstance(column, six.string_types):
-                column = channels.Facet(column)
-            facet = column
+            if isinstance(facet, six.string_types):
+                facet = channels.Facet(facet)
         else:
             facet = FacetMapping(row=row, column=column)
 


Test patch:
diff --git a/altair/vegalite/v3/tests/test_api.py b/altair/vegalite/v3/tests/test_api.py
index 119ed6eb..296fe437 100644
--- a/altair/vegalite/v3/tests/test_api.py
+++ b/altair/vegalite/v3/tests/test_api.py
@@ -215,6 +215,38 @@ def test_save(format, basic_chart):
         assert content.startswith('<!DOCTYPE html>')
 
 
+def test_facet():
+    # wrapped facet
+    chart1 = alt.Chart('data.csv').mark_point().encode(
+        x='x:Q',
+        y='y:Q',
+    ).facet(
+        'category:N',
+        columns=2
+    )
+
+    dct1 = chart1.to_dict()
+
+    assert dct1['facet'] == alt.Facet('category:N').to_dict()
+    assert dct1['columns'] == 2
+    assert dct1['data'] == alt.UrlData('data.csv').to_dict()
+
+    # explicit row/col facet
+    chart2 = alt.Chart('data.csv').mark_point().encode(
+        x='x:Q',
+        y='y:Q',
+    ).facet(
+        row='category1:Q',
+        column='category2:Q'
+    )
+
+    dct2 = chart2.to_dict()
+
+    assert dct2['facet']['row'] == alt.Facet('category1:Q').to_dict()
+    assert dct2['facet']['column'] == alt.Facet('category2:Q').to_dict()
+    assert 'columns' not in dct2
+    assert dct2['data'] == alt.UrlData('data.csv').to_dict()
+
 def test_facet_parse():
     chart = alt.Chart('data.csv').mark_point().encode(
         x='x:Q',
@@ -491,9 +523,11 @@ def test_chart_from_dict():
               base + base,
               base | base,
               base & base,
-              base.facet(row='c:N'),
+              base.facet('c:N'),
               (base + base).facet(row='c:N', data='data.csv'),
-              base.repeat(['c:N', 'd:N'])]
+              base.repeat(['c', 'd']),
+              (base + base).repeat(row=['c', 'd'])
+    ]
 
     for chart in charts:
         print(chart)
Repository: altair-viz/altair
Pull request #1525 (2019-05-22 02:27:31)

BUG: make it possible to pass data as a property


Patch:
diff --git a/altair/vegalite/v3/api.py b/altair/vegalite/v3/api.py
index f8ec4a19..84cd23b7 100644
--- a/altair/vegalite/v3/api.py
+++ b/altair/vegalite/v3/api.py
@@ -537,7 +537,9 @@ class TopLevelMixin(mixins.ConfigMethodMixin):
                 # For backward compatibility with old selection interface.
                 setattr(copy, key, {val.name: val.selection})
             else:
-                self.validate_property(key, val)
+                # Don't validate data, because it hasn't been processed.
+                if key != 'data':
+                    self.validate_property(key, val)
                 setattr(copy, key, val)
         return copy
 


Test patch:
diff --git a/altair/vegalite/v3/tests/test_api.py b/altair/vegalite/v3/tests/test_api.py
index 296fe437..426a8555 100644
--- a/altair/vegalite/v3/tests/test_api.py
+++ b/altair/vegalite/v3/tests/test_api.py
@@ -640,3 +640,11 @@ def test_repeat():
     assert 'columns' not in dct2
     assert dct2['spec']['encoding']['x']['field'] == {'repeat': 'row'}
     assert dct2['spec']['encoding']['y']['field'] == {'repeat': 'column'}
+
+
+def test_data_property():
+    data = pd.DataFrame({'x': [1, 2, 3], 'y': list('ABC')})
+    chart1 = alt.Chart(data).mark_point()
+    chart2 = alt.Chart().mark_point().properties(data=data)
+
+    assert chart1.to_dict() == chart2.to_dict()
\ No newline at end of file
Repository: altair-viz/altair
Pull request #1686 (2019-09-06 19:47:22)

Add API wrapper for sphere generator
I noticed the sphere generator was missing from the data gen wrappers. 


Patch:
diff --git a/altair/vegalite/v3/api.py b/altair/vegalite/v3/api.py
index b19cebca..faf2b73c 100644
--- a/altair/vegalite/v3/api.py
+++ b/altair/vegalite/v3/api.py
@@ -94,7 +94,8 @@ def _prepare_data(data, context):
     # if data is still not a recognized type, then return
     if not isinstance(data, (dict, core.Data, core.UrlData,
                              core.InlineData, core.NamedData,
-                             core.GraticuleGenerator, core.SequenceGenerator)):
+                             core.GraticuleGenerator, core.SequenceGenerator,
+                             core.SphereGenerator)):
         warnings.warn("data of type {} not recognized".format(type(data)))
 
     return data
@@ -1984,4 +1985,9 @@ def graticule(**kwds):
       graticule = True
   else:
       graticule = core.GraticuleParams(**kwds)
-  return core.GraticuleGenerator(graticule=graticule)
\ No newline at end of file
+  return core.GraticuleGenerator(graticule=graticule)
+
+
+def sphere():
+  """Sphere generator."""
+  return core.SphereGenerator(sphere=True)
diff --git a/doc/user_guide/data.rst b/doc/user_guide/data.rst
index ff9fdc02..8ffc303e 100644
--- a/doc/user_guide/data.rst
+++ b/doc/user_guide/data.rst
@@ -276,6 +276,24 @@ lines on a geographic visualization, known as a graticule. These can be created
        rotate=[0, -45, 0]
    )
 
+Sphere Generator
+^^^^^^^^^^^^^^^^
+Finally when visualizing the globe a sphere can be used as a background layer
+within a map to represent the extent of the Earth. This sphere data can be
+created using Altair's :func:`sphere` generator function. Here is an example:
+
+.. altair-plot::
+
+   import altair as alt
+
+   sphere_data = alt.sphere()
+   grat_data = alt.graticule(step=[15, 15])
+
+   background = alt.Chart(sphere_data).mark_geoshape(fill='aliceblue')
+   lines = alt.Chart(grat_data).mark_geoshape(stroke='lightgrey')
+
+   alt.layer(background, lines).project('naturalEarth1')
+
 .. _Pandas: http://pandas.pydata.org/
 .. _Pandas pivot documentation: https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.pivot.html
 .. _Pandas melt documentation: https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.melt.html#pandas.DataFrame.melt


Test patch:
diff --git a/altair/vegalite/v3/tests/test_api.py b/altair/vegalite/v3/tests/test_api.py
index f556376d..c8d7995c 100644
--- a/altair/vegalite/v3/tests/test_api.py
+++ b/altair/vegalite/v3/tests/test_api.py
@@ -858,4 +858,9 @@ def test_graticule():
     assert data.to_dict() == {'graticule': True}
 
     data = alt.graticule(step=[15, 15])
-    assert data.to_dict() == {'graticule': {'step': [15, 15]}}
\ No newline at end of file
+    assert data.to_dict() == {'graticule': {'step': [15, 15]}}
+
+
+def test_sphere():
+    data = alt.sphere()
+    assert data.to_dict() == {'sphere': True}
Repository: altair-viz/altair
Pull request #2683 (2022-09-22 22:31:25)

MAINT: Replace `iteritems` with `items`
`iteritems` is raising a warning in pandas >=1.5 [as it is now deprecated](https://pandas.pydata.org/pandas-docs/stable/whatsnew/v1.5.0.html#other-deprecations). The recommended replacement is `items`.



Patch:
diff --git a/altair/utils/core.py b/altair/utils/core.py
index 43bc1b4d..588e8c60 100644
--- a/altair/utils/core.py
+++ b/altair/utils/core.py
@@ -314,7 +314,7 @@ def sanitize_dataframe(df):  # noqa: C901
         else:
             return val
 
-    for col_name, dtype in df.dtypes.iteritems():
+    for col_name, dtype in df.dtypes.items():
         if str(dtype) == "category":
             # XXXX: work around bug in to_json for categorical types
             # https://github.com/pydata/pandas/issues/10778


Test patch:
diff --git a/altair/utils/tests/test_utils.py b/altair/utils/tests/test_utils.py
index 1ccede41..54a12721 100644
--- a/altair/utils/tests/test_utils.py
+++ b/altair/utils/tests/test_utils.py
@@ -129,9 +129,9 @@ def test_sanitize_nullable_integers():
     )
 
     df_clean = sanitize_dataframe(df)
-    assert {col.dtype.name for _, col in df_clean.iteritems()} == {"object"}
+    assert {col.dtype.name for _, col in df_clean.items()} == {"object"}
 
-    result_python = {col_name: list(col) for col_name, col in df_clean.iteritems()}
+    result_python = {col_name: list(col) for col_name, col in df_clean.items()}
     assert result_python == {
         "int_np": [1, 2, 3, 4, 5],
         "int64": [1, 2, 3, None, 5],
@@ -157,9 +157,9 @@ def test_sanitize_string_dtype():
     )
 
     df_clean = sanitize_dataframe(df)
-    assert {col.dtype.name for _, col in df_clean.iteritems()} == {"object"}
+    assert {col.dtype.name for _, col in df_clean.items()} == {"object"}
 
-    result_python = {col_name: list(col) for col_name, col in df_clean.iteritems()}
+    result_python = {col_name: list(col) for col_name, col in df_clean.items()}
     assert result_python == {
         "string_object": ["a", "b", "c", "d"],
         "string_string": ["a", "b", "c", "d"],
@@ -182,9 +182,9 @@ def test_sanitize_boolean_dtype():
     )
 
     df_clean = sanitize_dataframe(df)
-    assert {col.dtype.name for _, col in df_clean.iteritems()} == {"object"}
+    assert {col.dtype.name for _, col in df_clean.items()} == {"object"}
 
-    result_python = {col_name: list(col) for col_name, col in df_clean.iteritems()}
+    result_python = {col_name: list(col) for col_name, col in df_clean.items()}
     assert result_python == {
         "bool_none": [True, False, None],
         "none": [None, None, None],
Repository: altair-viz/altair
Pull request #2979 (2023-03-20 20:59:16)

Display more helpful error message when two fields strings are used in condition
This is an attempt to resolve https://github.com/altair-viz/altair/issues/2935.

If there are other string that can be passed to conditions that are not references for fields, then that would be an issue for this PR, but I can't think of any on the top of my head. It could also be argued that maybe this should be fixed in VegaLite, but it seems helpful and straightforward to fix it directly in Altair.



Patch:
diff --git a/altair/vegalite/v5/api.py b/altair/vegalite/v5/api.py
index 8af92660..a1b3388e 100644
--- a/altair/vegalite/v5/api.py
+++ b/altair/vegalite/v5/api.py
@@ -773,8 +773,13 @@ def condition(predicate, if_true, if_false, **kwargs):
         # dict in the appropriate schema
         if_true = if_true.to_dict()
     elif isinstance(if_true, str):
-        if_true = utils.parse_shorthand(if_true)
-        if_true.update(kwargs)
+        if isinstance(if_false, str):
+            raise ValueError(
+                "A field cannot be used for both the `if_true` and `if_false` values of a condition. One of them has to specify a `value` or `datum` definition."
+            )
+        else:
+            if_true = utils.parse_shorthand(if_true)
+            if_true.update(kwargs)
     condition.update(if_true)
 
     if isinstance(if_false, core.SchemaBase):
diff --git a/doc/releases/changes.rst b/doc/releases/changes.rst
index 9dda323c..14168e59 100644
--- a/doc/releases/changes.rst
+++ b/doc/releases/changes.rst
@@ -24,6 +24,7 @@ Enhancements
 - Substantially improved error handling. Both in terms of finding the more relevant error (#2842), and in terms of improving the formatting and clarity of the error messages (#2824, #2568).
 - Include experimental support for the DataFrame Interchange Protocol (through ``__dataframe__`` attribute). This requires ``pyarrow>=11.0.0`` (#2888).
 - Support data type inference for columns with special characters (#2905).
+- Changed error message to be more informative when two field strings are using in a condition (#2979).
 
 Grammar Changes
 ~~~~~~~~~~~~~~~


Test patch:
diff --git a/tests/utils/test_schemapi.py b/tests/utils/test_schemapi.py
index 35f14ce2..8bc11b09 100644
--- a/tests/utils/test_schemapi.py
+++ b/tests/utils/test_schemapi.py
@@ -610,6 +610,20 @@ def test_chart_validation_errors(chart_func, expected_error_message):
         chart.to_dict()
 
 
+def test_multiple_field_strings_in_condition():
+    selection = alt.selection_point()
+    expected_error_message = "A field cannot be used for both the `if_true` and `if_false` values of a condition. One of them has to specify a `value` or `datum` definition."
+    with pytest.raises(ValueError, match=expected_error_message):
+        (
+            alt.Chart(data.cars())
+            .mark_circle()
+            .add_params(selection)
+            .encode(
+                color=alt.condition(selection, "Origin", "Origin"),
+            )
+        ).to_dict()
+
+
 def test_serialize_numpy_types():
     m = MySchema(
         a={"date": np.datetime64("2019-01-01")},
Repository: altair-viz/altair
Pull request #3179 (2023-08-29 21:26:23)

Jonmmease/infer dtype pandas fallback
Closes https://github.com/altair-viz/altair/issues/3177.

This PR updates the `parse_shorthand` type inference logic to fall back to the pandas-based implementation (that which was always used in Altair 5.0) when the DataFrame Interchange approach raises a `NotImplementedError`.


Patch:
diff --git a/altair/utils/core.py b/altair/utils/core.py
index 9fecc4aa..f2ce1d4b 100644
--- a/altair/utils/core.py
+++ b/altair/utils/core.py
@@ -586,7 +586,15 @@ def parse_shorthand(
                 unescaped_field = attrs["field"].replace("\\", "")
                 if unescaped_field in dfi.column_names():
                     column = dfi.get_column_by_name(unescaped_field)
-                    attrs["type"] = infer_vegalite_type_for_dfi_column(column)
+                    try:
+                        attrs["type"] = infer_vegalite_type_for_dfi_column(column)
+                    except NotImplementedError:
+                        # Fall back to pandas-based inference
+                        if isinstance(data, pd.DataFrame):
+                            attrs["type"] = infer_vegalite_type(data[unescaped_field])
+                        else:
+                            raise
+
                     if isinstance(attrs["type"], tuple):
                         attrs["sort"] = attrs["type"][1]
                         attrs["type"] = attrs["type"][0]


Test patch:
diff --git a/tests/utils/test_core.py b/tests/utils/test_core.py
index 62203524..75db1876 100644
--- a/tests/utils/test_core.py
+++ b/tests/utils/test_core.py
@@ -134,7 +134,8 @@ def test_parse_shorthand():
     )
 
 
-def test_parse_shorthand_with_data():
+@pytest.mark.parametrize("object_dtype", [False, True])
+def test_parse_shorthand_with_data(object_dtype):
     def check(s, data, **kwargs):
         assert parse_shorthand(s, data) == kwargs
 
@@ -147,6 +148,9 @@ def test_parse_shorthand_with_data():
         }
     )
 
+    if object_dtype:
+        data = data.astype("object")
+
     check("x", data, field="x", type="quantitative")
     check("y", data, field="y", type="nominal")
     check("z", data, field="z", type="temporal")