Repository: pandas-dev/pandas

Issue:
DOC: Add comparison with SPSS
### Pandas version checks

- [x] I have checked that the issue still exists on the latest versions of the docs on `main` [here](https://pandas.pydata.org/docs/dev/)


### Location of the documentation

https://pandas.pydata.org/docs/getting_started/comparison/index.html

### Documentation problem

I teach pandas, and have a number of students that come in with experience in [SPSS](https://en.wikipedia.org/wiki/SPSS). We have comparison guides with a number of other related tools, but none that speaks to SPSS.

### Suggested fix for documentation

Add a "Comparison to SPSS" page

Discussion:

Patch:
diff --git a/.gitignore b/.gitignore
index a188e216d9..d951f3fb9c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -137,3 +137,7 @@ doc/source/savefig/
 # Interactive terminal generated files #
 ########################################
 .jupyterlite.doit.db
+
+# Pyodide/WASM related files #
+##############################
+/.pyodide-xbuildenv-*
diff --git a/ci/code_checks.sh b/ci/code_checks.sh
index 948d8bee8b..c7e644bd30 100755
--- a/ci/code_checks.sh
+++ b/ci/code_checks.sh
@@ -79,12 +79,10 @@ if [[ -z "$CHECK" || "$CHECK" == "docstrings" ]]; then
         -i "pandas.Timestamp.min PR02" \
         -i "pandas.Timestamp.resolution PR02" \
         -i "pandas.Timestamp.tzinfo GL08" \
-        -i "pandas.arrays.TimedeltaArray PR07,SA01" \
         -i "pandas.core.groupby.DataFrameGroupBy.plot PR02" \
         -i "pandas.core.groupby.SeriesGroupBy.plot PR02" \
         -i "pandas.core.resample.Resampler.quantile PR01,PR07" \
         -i "pandas.core.resample.Resampler.transform PR01,RT03,SA01" \
-        -i "pandas.plotting.andrews_curves RT03,SA01" \
         -i "pandas.tseries.offsets.BDay PR02,SA01" \
         -i "pandas.tseries.offsets.BQuarterBegin.is_on_offset GL08" \
         -i "pandas.tseries.offsets.BQuarterBegin.n GL08" \
diff --git a/ci/deps/actions-311-pyarrownightly.yaml b/ci/deps/actions-311-pyarrownightly.yaml
index 22e4907e5a..2d3d11c294 100644
--- a/ci/deps/actions-311-pyarrownightly.yaml
+++ b/ci/deps/actions-311-pyarrownightly.yaml
@@ -23,7 +23,7 @@ dependencies:
 
   - pip:
     - "tzdata>=2022.7"
-    - "--extra-index-url https://pypi.fury.io/arrow-nightlies/"
+    - "--extra-index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple"
     - "--prefer-binary"
     - "--pre"
     - "pyarrow"
diff --git a/doc/source/getting_started/comparison/comparison_with_spss.rst b/doc/source/getting_started/comparison/comparison_with_spss.rst
new file mode 100644
index 0000000000..12c64bfd18
--- /dev/null
+++ b/doc/source/getting_started/comparison/comparison_with_spss.rst
@@ -0,0 +1,229 @@
+.. _compare_with_spss:
+
+{{ header }}
+
+Comparison with SPSS
+********************
+For potential users coming from `SPSS <https://www.ibm.com/spss>`__, this page is meant to demonstrate
+how various SPSS operations would be performed using pandas.
+
+.. include:: includes/introduction.rst
+
+Data structures
+---------------
+
+General terminology translation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. csv-table::
+    :header: "pandas", "SPSS"
+    :widths: 20, 20
+
+    :class:`DataFrame`, data file
+    column, variable
+    row, case
+    groupby, split file
+    :class:`NaN`, system-missing
+
+:class:`DataFrame`
+~~~~~~~~~~~~~~~~~~
+
+A :class:`DataFrame` in pandas is analogous to an SPSS data file - a two-dimensional
+data source with labeled columns that can be of different types. As will be shown in this
+document, almost any operation that can be performed in SPSS can also be accomplished in pandas.
+
+:class:`Series`
+~~~~~~~~~~~~~~~
+
+A :class:`Series` is the data structure that represents one column of a :class:`DataFrame`. SPSS doesn't have a
+separate data structure for a single variable, but in general, working with a :class:`Series` is analogous
+to working with a variable in SPSS.
+
+:class:`Index`
+~~~~~~~~~~~~~~
+
+Every :class:`DataFrame` and :class:`Series` has an :class:`Index` -- labels on the *rows* of the data. SPSS does not
+have an exact analogue, as cases are simply numbered sequentially from 1. In pandas, if no index is
+specified, a :class:`RangeIndex` is used by default (first row = 0, second row = 1, and so on).
+
+While using a labeled :class:`Index` or :class:`MultiIndex` can enable sophisticated analyses and is ultimately an
+important part of pandas to understand, for this comparison we will essentially ignore the :class:`Index` and
+just treat the :class:`DataFrame` as a collection of columns. Please see the :ref:`indexing documentation<indexing>`
+for much more on how to use an :class:`Index` effectively.
+
+
+Copies vs. in place operations
+------------------------------
+
+.. include:: includes/copies.rst
+
+
+Data input / output
+-------------------
+
+Reading external data
+~~~~~~~~~~~~~~~~~~~~~
+
+Like SPSS, pandas provides utilities for reading in data from many formats. The ``tips`` dataset, found within
+the pandas tests (`csv <https://raw.githubusercontent.com/pandas-dev/pandas/main/pandas/tests/io/data/csv/tips.csv>`_)
+will be used in many of the following examples.
+
+In SPSS, you would use File > Open > Data to import a CSV file:
+
+.. code-block:: text
+
+    FILE > OPEN > DATA
+    /TYPE=CSV
+    /FILE='tips.csv'
+    /DELIMITERS=","
+    /FIRSTCASE=2
+    /VARIABLES=col1 col2 col3.
+
+The pandas equivalent would use :func:`read_csv`:
+
+.. code-block:: python
+
+   url = (
+       "https://raw.githubusercontent.com/pandas-dev"
+       "/pandas/main/pandas/tests/io/data/csv/tips.csv"
+   )
+   tips = pd.read_csv(url)
+   tips
+
+Like SPSS's data import wizard, ``read_csv`` can take a number of parameters to specify how the data should be parsed.
+For example, if the data was instead tab delimited, and did not have column names, the pandas command would be:
+
+.. code-block:: python
+
+   tips = pd.read_csv("tips.csv", sep="\t", header=None)
+
+   # alternatively, read_table is an alias to read_csv with tab delimiter
+   tips = pd.read_table("tips.csv", header=None)
+
+
+Data operations
+---------------
+
+Filtering
+~~~~~~~~~
+
+In SPSS, filtering is done through Data > Select Cases:
+
+.. code-block:: text
+
+    SELECT IF (total_bill > 10).
+    EXECUTE.
+
+In pandas, boolean indexing can be used:
+
+.. code-block:: python
+
+    tips[tips["total_bill"] > 10]
+
+
+Sorting
+~~~~~~~
+
+In SPSS, sorting is done through Data > Sort Cases:
+
+.. code-block:: text
+
+    SORT CASES BY sex total_bill.
+    EXECUTE.
+
+In pandas, this would be written as:
+
+.. code-block:: python
+
+    tips.sort_values(["sex", "total_bill"])
+
+
+String processing
+-----------------
+
+Finding length of string
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+In SPSS:
+
+.. code-block:: text
+
+    COMPUTE length = LENGTH(time).
+    EXECUTE.
+
+.. include:: includes/length.rst
+
+
+Changing case
+~~~~~~~~~~~~~
+
+In SPSS:
+
+.. code-block:: text
+
+    COMPUTE upper = UPCASE(time).
+    COMPUTE lower = LOWER(time).
+    EXECUTE.
+
+.. include:: includes/case.rst
+
+
+Merging
+-------
+
+In SPSS, merging data files is done through Data > Merge Files.
+
+.. include:: includes/merge_setup.rst
+.. include:: includes/merge.rst
+
+
+GroupBy operations
+------------------
+
+Split-file processing
+~~~~~~~~~~~~~~~~~~~~~
+
+In SPSS, split-file analysis is done through Data > Split File:
+
+.. code-block:: text
+
+    SORT CASES BY sex.
+    SPLIT FILE BY sex.
+    DESCRIPTIVES VARIABLES=total_bill tip
+      /STATISTICS=MEAN STDDEV MIN MAX.
+
+The pandas equivalent would be:
+
+.. code-block:: python
+
+    tips.groupby("sex")[["total_bill", "tip"]].agg(["mean", "std", "min", "max"])
+
+
+Missing data
+------------
+
+SPSS uses the period (``.``) for numeric missing values and blank spaces for string missing values.
+pandas uses ``NaN`` (Not a Number) for numeric missing values and ``None`` or ``NaN`` for string
+missing values.
+
+.. include:: includes/missing.rst
+
+
+Other considerations
+--------------------
+
+Output management
+-----------------
+
+While pandas does not have a direct equivalent to SPSS's Output Management System (OMS), you can
+capture and export results in various ways:
+
+.. code-block:: python
+
+   # Save summary statistics to CSV
+   tips.groupby('sex')[['total_bill', 'tip']].mean().to_csv('summary.csv')
+
+   # Save multiple results to Excel sheets
+   with pd.ExcelWriter('results.xlsx') as writer:
+       tips.describe().to_excel(writer, sheet_name='Descriptives')
+       tips.groupby('sex').mean().to_excel(writer, sheet_name='Means by Gender')
diff --git a/doc/source/getting_started/comparison/index.rst b/doc/source/getting_started/comparison/index.rst
index c3f58ce1f3..3133d74afa 100644
--- a/doc/source/getting_started/comparison/index.rst
+++ b/doc/source/getting_started/comparison/index.rst
@@ -14,3 +14,4 @@ Comparison with other tools
     comparison_with_spreadsheets
     comparison_with_sas
     comparison_with_stata
+    comparison_with_spss
diff --git a/doc/source/whatsnew/v2.3.0.rst b/doc/source/whatsnew/v2.3.0.rst
index 96eed72823..de1118b56d 100644
--- a/doc/source/whatsnew/v2.3.0.rst
+++ b/doc/source/whatsnew/v2.3.0.rst
@@ -95,7 +95,7 @@ Timezones
 
 Numeric
 ^^^^^^^
--
+- Enabled :class:`Series.mode` and :class:`DataFrame.mode` with ``dropna=False`` to sort the result for all dtypes in the presence of NA values; previously only certain dtypes would sort (:issue:`60702`)
 -
 
 Conversion
@@ -105,6 +105,7 @@ Conversion
 
 Strings
 ^^^^^^^
+- Bug in :meth:`Series.__pos__` and :meth:`DataFrame.__pos__` did not raise for :class:`StringDtype` with ``storage="pyarrow"`` (:issue:`60710`)
 - Bug in :meth:`Series.rank` for :class:`StringDtype` with ``storage="pyarrow"`` incorrectly returning integer results in case of ``method="average"`` and raising an error if it would truncate results (:issue:`59768`)
 - Bug in :meth:`Series.replace` with :class:`StringDtype` when replacing with a non-string value was not upcasting to ``object`` dtype (:issue:`60282`)
 - Bug in :meth:`Series.str.replace` when ``n < 0`` for :class:`StringDtype` with ``storage="pyarrow"`` (:issue:`59628`)
diff --git a/pandas/_libs/groupby.pyx b/pandas/_libs/groupby.pyx
index fd288dff01..70af22f514 100644
--- a/pandas/_libs/groupby.pyx
+++ b/pandas/_libs/groupby.pyx
@@ -712,7 +712,7 @@ def group_sum(
         int64_t[:, ::1] nobs
         Py_ssize_t len_values = len(values), len_labels = len(labels)
         bint uses_mask = mask is not None
-        bint isna_entry
+        bint isna_entry, isna_result
 
     if len_values != len_labels:
         raise ValueError("len(index) != len(labels)")
@@ -744,20 +744,18 @@ def group_sum(
             for j in range(K):
                 val = values[i, j]
 
-                if not skipna and (
-                    (uses_mask and result_mask[lab, j]) or
-                    (is_datetimelike and sumx[lab, j] == NPY_NAT) or
-                    _treat_as_na(sumx[lab, j], False)
-                ):
-                    # If sum is already NA, don't add to it. This is important for
-                    # datetimelikebecause adding a value to NPY_NAT may not result
-                    # in a NPY_NAT
-                    continue
-
                 if uses_mask:
                     isna_entry = mask[i, j]
+                    isna_result = result_mask[lab, j]
                 else:
                     isna_entry = _treat_as_na(val, is_datetimelike)
+                    isna_result = _treat_as_na(sumx[lab, j], is_datetimelike)
+
+                if not skipna and isna_result:
+                    # If sum is already NA, don't add to it. This is important for
+                    # datetimelikebecause adding a value to NPY_NAT may not result
+                    # in a NPY_NAT
+                    continue
 
                 if not isna_entry:
                     nobs[lab, j] += 1
diff --git a/pandas/compat/__init__.py b/pandas/compat/__init__.py
index e767438640..138456f877 100644
--- a/pandas/compat/__init__.py
+++ b/pandas/compat/__init__.py
@@ -34,6 +34,7 @@ from pandas.compat.pyarrow import (
     pa_version_under16p0,
     pa_version_under17p0,
     pa_version_under18p0,
+    pa_version_under19p0,
 )
 
 if TYPE_CHECKING:
@@ -166,4 +167,5 @@ __all__ = [
     "pa_version_under16p0",
     "pa_version_under17p0",
     "pa_version_under18p0",
+    "pa_version_under19p0",
 ]
diff --git a/pandas/compat/pyarrow.py b/pandas/compat/pyarrow.py
index bd009b544f..c501c06b93 100644
--- a/pandas/compat/pyarrow.py
+++ b/pandas/compat/pyarrow.py
@@ -18,6 +18,7 @@ try:
     pa_version_under16p0 = _palv < Version("16.0.0")
     pa_version_under17p0 = _palv < Version("17.0.0")
     pa_version_under18p0 = _palv < Version("18.0.0")
+    pa_version_under19p0 = _palv < Version("19.0.0")
     HAS_PYARROW = True
 except ImportError:
     pa_version_under10p1 = True
@@ -30,4 +31,5 @@ except ImportError:
     pa_version_under16p0 = True
     pa_version_under17p0 = True
     pa_version_under18p0 = True
+    pa_version_under19p0 = True
     HAS_PYARROW = False
diff --git a/pandas/core/arrays/string_arrow.py b/pandas/core/arrays/string_arrow.py
index 27c1425d11..d35083fd89 100644
--- a/pandas/core/arrays/string_arrow.py
+++ b/pandas/core/arrays/string_arrow.py
@@ -481,6 +481,9 @@ class ArrowStringArray(ObjectStringArrayMixin, ArrowExtensionArray, BaseStringAr
                 return result.to_numpy(np.bool_, na_value=False)
         return result
 
+    def __pos__(self) -> Self:
+        raise TypeError(f"bad operand type for unary +: '{self.dtype}'")
+
 
 class ArrowStringArrayNumpySemantics(ArrowStringArray):
     _na_value = np.nan
diff --git a/pandas/core/arrays/timedeltas.py b/pandas/core/arrays/timedeltas.py
index a8a0037d0b..c5b3129c50 100644
--- a/pandas/core/arrays/timedeltas.py
+++ b/pandas/core/arrays/timedeltas.py
@@ -115,10 +115,10 @@ class TimedeltaArray(dtl.TimelikeOps):
     ----------
     data : array-like
         The timedelta data.
-
     dtype : numpy.dtype
         Currently, only ``numpy.dtype("timedelta64[ns]")`` is accepted.
     freq : Offset, optional
+        Frequency of the data.
     copy : bool, default False
         Whether to copy the underlying array of data.
 
@@ -130,6 +130,12 @@ class TimedeltaArray(dtl.TimelikeOps):
     -------
     None
 
+    See Also
+    --------
+    Timedelta : Represents a duration, the difference between two dates or times.
+    TimedeltaIndex : Immutable Index of timedelta64 data.
+    to_timedelta : Convert argument to timedelta.
+
     Examples
     --------
     >>> pd.arrays.TimedeltaArray._from_sequence(pd.TimedeltaIndex(["1h", "2h"]))
diff --git a/pandas/io/_util.py b/pandas/io/_util.py
index 9778a404e2..6827fbe9c9 100644
--- a/pandas/io/_util.py
+++ b/pandas/io/_util.py
@@ -10,7 +10,10 @@ import numpy as np
 from pandas._config import using_string_dtype
 
 from pandas._libs import lib
-from pandas.compat import pa_version_under18p0
+from pandas.compat import (
+    pa_version_under18p0,
+    pa_version_under19p0,
+)
 from pandas.compat._optional import import_optional_dependency
 
 import pandas as pd
@@ -77,7 +80,10 @@ def arrow_table_to_pandas(
     elif dtype_backend == "pyarrow":
         types_mapper = pd.ArrowDtype
     elif using_string_dtype():
-        types_mapper = _arrow_string_types_mapper()
+        if pa_version_under19p0:
+            types_mapper = _arrow_string_types_mapper()
+        else:
+            types_mapper = None
     elif dtype_backend is lib.no_default or dtype_backend == "numpy":
         types_mapper = None
     else:
diff --git a/pandas/plotting/_misc.py b/pandas/plotting/_misc.py
index b20f8ac5f4..3f839cefe7 100644
--- a/pandas/plotting/_misc.py
+++ b/pandas/plotting/_misc.py
@@ -389,6 +389,12 @@ def andrews_curves(
     Returns
     -------
     :class:`matplotlib.axes.Axes`
+        The matplotlib Axes object with the plot.
+
+    See Also
+    --------
+    plotting.parallel_coordinates : Plot parallel coordinates chart.
+    DataFrame.plot : Make plots of Series or DataFrame.
 
     Examples
     --------
diff --git a/web/pandas/pdeps/0010-required-pyarrow-dependency.md b/web/pandas/pdeps/0010-required-pyarrow-dependency.md
index d586c46e24..0c3bf3c776 100644
--- a/web/pandas/pdeps/0010-required-pyarrow-dependency.md
+++ b/web/pandas/pdeps/0010-required-pyarrow-dependency.md
@@ -185,7 +185,6 @@ Additionally, if a user is installing pandas in an environment where wheels are
 the user will need to also build Arrow C++ and related dependencies when installing from source. These environments include
 
 - Alpine linux (commonly used as a base for Docker containers)
-- WASM (pyodide and pyscript)
 - Python development versions
 
 Lastly, pandas development and releases will need to be mindful of PyArrow's development and release cadance. For example when


Test patch:
diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml
index e0fa7f7421..842629ba33 100644
--- a/.github/workflows/unit-tests.yml
+++ b/.github/workflows/unit-tests.yml
@@ -84,9 +84,10 @@ jobs:
           - name: "Pyarrow Nightly"
             env_file: actions-311-pyarrownightly.yaml
             pattern: "not slow and not network and not single_cpu"
+            pandas_future_infer_string: "1"
             platform: ubuntu-22.04
       fail-fast: false
-    name: ${{ matrix.name || format('ubuntu-latest {0}', matrix.env_file) }}-${{ matrix.platform }}
+    name: ${{ matrix.name || format('{0} {1}', matrix.platform, matrix.env_file) }}
     env:
       PATTERN: ${{ matrix.pattern }}
       LANG: ${{ matrix.lang || 'C.UTF-8' }}
@@ -429,20 +430,20 @@ jobs:
         with:
           fetch-depth: 0
 
-      - name: Set up Python for Pyodide
+      - name: Set up Python for pyodide-build
         id: setup-python
         uses: actions/setup-python@v5
         with:
-          python-version: '3.11.3'
+          python-version: '3.12'
 
       - name: Set up Emscripten toolchain
         uses: mymindstorm/setup-emsdk@v14
         with:
-          version: '3.1.46'
+          version: '3.1.58'
           actions-cache-folder: emsdk-cache
 
       - name: Install pyodide-build
-        run: pip install "pyodide-build==0.25.1"
+        run: pip install "pyodide-build>=0.29.2"
 
       - name: Build pandas for Pyodide
         run: |
@@ -451,10 +452,13 @@ jobs:
       - name: Set up Node.js
         uses: actions/setup-node@v4
         with:
-          node-version: '18'
+          node-version: '20'
 
       - name: Set up Pyodide virtual environment
+        env:
+          pyodide-version: '0.27.1'
         run: |
+          pyodide xbuildenv install ${{ env.pyodide-version }}
           pyodide venv .venv-pyodide
           source .venv-pyodide/bin/activate
           pip install dist/*.whl
diff --git a/pandas/tests/arrays/string_/test_string.py b/pandas/tests/arrays/string_/test_string.py
index a32ac7db46..f875873863 100644
--- a/pandas/tests/arrays/string_/test_string.py
+++ b/pandas/tests/arrays/string_/test_string.py
@@ -10,7 +10,10 @@ import pytest
 
 from pandas._config import using_string_dtype
 
-from pandas.compat.pyarrow import pa_version_under12p0
+from pandas.compat.pyarrow import (
+    pa_version_under12p0,
+    pa_version_under19p0,
+)
 
 from pandas.core.dtypes.common import is_dtype_equal
 
@@ -539,7 +542,7 @@ def test_arrow_roundtrip(dtype, string_storage, using_infer_string):
         assert table.field("a").type == "large_string"
     with pd.option_context("string_storage", string_storage):
         result = table.to_pandas()
-    if dtype.na_value is np.nan and not using_string_dtype():
+    if dtype.na_value is np.nan and not using_infer_string:
         assert result["a"].dtype == "object"
     else:
         assert isinstance(result["a"].dtype, pd.StringDtype)
@@ -553,6 +556,21 @@ def test_arrow_roundtrip(dtype, string_storage, using_infer_string):
         assert result.loc[2, "a"] is result["a"].dtype.na_value
 
 
+@pytest.mark.filterwarnings("ignore:Passing a BlockManager:DeprecationWarning")
+def test_arrow_from_string(using_infer_string):
+    # not roundtrip,  but starting with pyarrow table without pandas metadata
+    pa = pytest.importorskip("pyarrow")
+    table = pa.table({"a": pa.array(["a", "b", None], type=pa.string())})
+
+    result = table.to_pandas()
+
+    if using_infer_string and not pa_version_under19p0:
+        expected = pd.DataFrame({"a": ["a", "b", None]}, dtype="str")
+    else:
+        expected = pd.DataFrame({"a": ["a", "b", None]}, dtype="object")
+    tm.assert_frame_equal(result, expected)
+
+
 @pytest.mark.filterwarnings("ignore:Passing a BlockManager:DeprecationWarning")
 def test_arrow_load_from_zero_chunks(dtype, string_storage, using_infer_string):
     # GH-41040
diff --git a/pandas/tests/frame/test_unary.py b/pandas/tests/frame/test_unary.py
index 217255e73b..652f52bd22 100644
--- a/pandas/tests/frame/test_unary.py
+++ b/pandas/tests/frame/test_unary.py
@@ -3,9 +3,6 @@ from decimal import Decimal
 import numpy as np
 import pytest
 
-from pandas._config import using_string_dtype
-
-from pandas.compat import HAS_PYARROW
 from pandas.compat.numpy import np_version_gte1p25
 
 import pandas as pd
@@ -122,9 +119,6 @@ class TestDataFrameUnaryOperators:
         tm.assert_frame_equal(+df, df)
         tm.assert_series_equal(+df["a"], df["a"])
 
-    @pytest.mark.xfail(
-        using_string_dtype() and HAS_PYARROW, reason="TODO(infer_string)"
-    )
     @pytest.mark.filterwarnings("ignore:Applying:DeprecationWarning")
     def test_pos_object_raises(self):
         # GH#21380
diff --git a/pandas/tests/io/test_common.py b/pandas/tests/io/test_common.py
index 7ff3d24336..e162815271 100644
--- a/pandas/tests/io/test_common.py
+++ b/pandas/tests/io/test_common.py
@@ -23,6 +23,7 @@ from pandas.compat import (
     WASM,
     is_platform_windows,
 )
+from pandas.compat.pyarrow import pa_version_under19p0
 import pandas.util._test_decorators as td
 
 import pandas as pd
@@ -152,8 +153,8 @@ Look,a snake,🐍"""
         s = StringIO(data)
         with icom.get_handle(s, "rb", is_text=False) as handles:
             df = pa_csv.read_csv(handles.handle).to_pandas()
-            # TODO will have to update this when pyarrow' to_pandas() is fixed
-            expected = expected.astype("object")
+            if pa_version_under19p0:
+                expected = expected.astype("object")
             tm.assert_frame_equal(df, expected)
             assert not s.closed
 
diff --git a/pandas/tests/io/test_feather.py b/pandas/tests/io/test_feather.py
index 69354066dd..24af0a014d 100644
--- a/pandas/tests/io/test_feather.py
+++ b/pandas/tests/io/test_feather.py
@@ -6,7 +6,10 @@ import zoneinfo
 import numpy as np
 import pytest
 
-from pandas.compat.pyarrow import pa_version_under18p0
+from pandas.compat.pyarrow import (
+    pa_version_under18p0,
+    pa_version_under19p0,
+)
 
 import pandas as pd
 import pandas._testing as tm
@@ -239,16 +242,27 @@ class TestFeather:
             with pytest.raises(ValueError, match=msg):
                 read_feather(path, dtype_backend="numpy")
 
-    def test_string_inference(self, tmp_path):
+    def test_string_inference(self, tmp_path, using_infer_string):
         # GH#54431
         path = tmp_path / "test_string_inference.p"
         df = pd.DataFrame(data={"a": ["x", "y"]})
         df.to_feather(path)
         with pd.option_context("future.infer_string", True):
             result = read_feather(path)
+        dtype = pd.StringDtype(na_value=np.nan)
         expected = pd.DataFrame(
             data={"a": ["x", "y"]}, dtype=pd.StringDtype(na_value=np.nan)
         )
+        expected = pd.DataFrame(
+            data={"a": ["x", "y"]},
+            dtype=dtype,
+            columns=pd.Index(
+                ["a"],
+                dtype=object
+                if pa_version_under19p0 and not using_infer_string
+                else dtype,
+            ),
+        )
         tm.assert_frame_equal(result, expected)
 
     @pytest.mark.skipif(pa_version_under18p0, reason="not supported before 18.0")
diff --git a/pandas/tests/io/test_parquet.py b/pandas/tests/io/test_parquet.py
index 7919bb956d..91580c31ea 100644
--- a/pandas/tests/io/test_parquet.py
+++ b/pandas/tests/io/test_parquet.py
@@ -17,6 +17,7 @@ from pandas.compat.pyarrow import (
     pa_version_under13p0,
     pa_version_under15p0,
     pa_version_under17p0,
+    pa_version_under19p0,
 )
 
 import pandas as pd
@@ -254,8 +255,10 @@ def test_invalid_engine(df_compat):
         check_round_trip(df_compat, "foo", "bar")
 
 
-def test_options_py(df_compat, pa):
+def test_options_py(df_compat, pa, using_infer_string):
     # use the set option
+    if using_infer_string and not pa_version_under19p0:
+        df_compat.columns = df_compat.columns.astype("str")
 
     with pd.option_context("io.parquet.engine", "pyarrow"):
         check_round_trip(df_compat)
@@ -784,18 +787,21 @@ class TestParquetPyArrow(Base):
 
     def test_categorical(self, pa):
         # supported in >= 0.7.0
-        df = pd.DataFrame()
-        df["a"] = pd.Categorical(list("abcdef"))
-
-        # test for null, out-of-order values, and unobserved category
-        df["b"] = pd.Categorical(
-            ["bar", "foo", "foo", "bar", None, "bar"],
-            dtype=pd.CategoricalDtype(["foo", "bar", "baz"]),
-        )
-
-        # test for ordered flag
-        df["c"] = pd.Categorical(
-            ["a", "b", "c", "a", "c", "b"], categories=["b", "c", "d"], ordered=True
+        df = pd.DataFrame(
+            {
+                "a": pd.Categorical(list("abcdef")),
+                # test for null, out-of-order values, and unobserved category
+                "b": pd.Categorical(
+                    ["bar", "foo", "foo", "bar", None, "bar"],
+                    dtype=pd.CategoricalDtype(["foo", "bar", "baz"]),
+                ),
+                # test for ordered flag
+                "c": pd.Categorical(
+                    ["a", "b", "c", "a", "c", "b"],
+                    categories=["b", "c", "d"],
+                    ordered=True,
+                ),
+            }
         )
 
         check_round_trip(df, pa)
@@ -858,11 +864,13 @@ class TestParquetPyArrow(Base):
             repeat=1,
         )
 
-    def test_read_file_like_obj_support(self, df_compat):
+    def test_read_file_like_obj_support(self, df_compat, using_infer_string):
         pytest.importorskip("pyarrow")
         buffer = BytesIO()
         df_compat.to_parquet(buffer)
         df_from_buf = read_parquet(buffer)
+        if using_infer_string and not pa_version_under19p0:
+            df_compat.columns = df_compat.columns.astype("str")
         tm.assert_frame_equal(df_compat, df_from_buf)
 
     def test_expand_user(self, df_compat, monkeypatch):
@@ -929,7 +937,7 @@ class TestParquetPyArrow(Base):
                 "c": pd.Series(["a", None, "c"], dtype="string"),
             }
         )
-        if using_infer_string:
+        if using_infer_string and pa_version_under19p0:
             check_round_trip(df, pa, expected=df.astype({"c": "str"}))
         else:
             check_round_trip(df, pa)
@@ -943,7 +951,10 @@ class TestParquetPyArrow(Base):
         df = pd.DataFrame({"a": pd.Series(["a", None, "c"], dtype="string[pyarrow]")})
         with pd.option_context("string_storage", string_storage):
             if using_infer_string:
-                expected = df.astype("str")
+                if pa_version_under19p0:
+                    expected = df.astype("str")
+                else:
+                    expected = df.astype(f"string[{string_storage}]")
                 expected.columns = expected.columns.astype("str")
             else:
                 expected = df.astype(f"string[{string_storage}]")
@@ -1099,17 +1110,24 @@ class TestParquetPyArrow(Base):
         new_df = read_parquet(path, engine=pa)
         assert new_df.attrs == df.attrs
 
-    def test_string_inference(self, tmp_path, pa):
+    def test_string_inference(self, tmp_path, pa, using_infer_string):
         # GH#54431
         path = tmp_path / "test_string_inference.p"
         df = pd.DataFrame(data={"a": ["x", "y"]}, index=["a", "b"])
-        df.to_parquet(path, engine="pyarrow")
+        df.to_parquet(path, engine=pa)
         with pd.option_context("future.infer_string", True):
-            result = read_parquet(path, engine="pyarrow")
+            result = read_parquet(path, engine=pa)
+        dtype = pd.StringDtype(na_value=np.nan)
         expected = pd.DataFrame(
             data={"a": ["x", "y"]},
-            dtype=pd.StringDtype(na_value=np.nan),
-            index=pd.Index(["a", "b"], dtype=pd.StringDtype(na_value=np.nan)),
+            dtype=dtype,
+            index=pd.Index(["a", "b"], dtype=dtype),
+            columns=pd.Index(
+                ["a"],
+                dtype=object
+                if pa_version_under19p0 and not using_infer_string
+                else dtype,
+            ),
         )
         tm.assert_frame_equal(result, expected)
 
@@ -1122,7 +1140,10 @@ class TestParquetPyArrow(Base):
         df = pd.DataFrame({"a": [Decimal("123.00")]}, dtype="string[pyarrow]")
         df.to_parquet(path, schema=pa.schema([("a", pa.decimal128(5))]))
         result = read_parquet(path)
-        expected = pd.DataFrame({"a": ["123"]}, dtype="string[python]")
+        if pa_version_under19p0:
+            expected = pd.DataFrame({"a": ["123"]}, dtype="string[python]")
+        else:
+            expected = pd.DataFrame({"a": [Decimal("123.00")]}, dtype="object")
         tm.assert_frame_equal(result, expected)
 
     def test_infer_string_large_string_type(self, tmp_path, pa):
diff --git a/pandas/tests/series/indexing/test_setitem.py b/pandas/tests/series/indexing/test_setitem.py
index 158198239b..49c933c308 100644
--- a/pandas/tests/series/indexing/test_setitem.py
+++ b/pandas/tests/series/indexing/test_setitem.py
@@ -9,7 +9,6 @@ import os
 import numpy as np
 import pytest
 
-from pandas.compat import WASM
 from pandas.compat.numpy import np_version_gte1p24
 from pandas.errors import IndexingError
 
@@ -1449,7 +1448,6 @@ class TestCoercionFloat64(CoercionTest):
                         np_version_gte1p24
                         and os.environ.get("NPY_PROMOTION_STATE", "weak") != "weak"
                     )
-                    or WASM
                 ),
                 reason="np.float32(1.1) ends up as 1.100000023841858, so "
                 "np_can_hold_element raises and we cast to float64",