Skip to content

Table.from_df raises TypeError for pandas string columns with missing values (pd.NA) #20663

Open

Description

@taldcroft

Description

Table.from_df fails on any pandas DataFrame that has a string-like column with a
missing value represented as pd.NA. That includes the common case of a pandas
string dtype column (dtype="string", either the python or pyarrow storage) with a
missing entry:

TypeError: must be real number, not NAType

The cause is the check in astropy/table/_dataframes.py that decides whether an
object column can be converted to a native NumPy string array:

if all((x is None) or isinstance(x, ts) or math.isnan(x) for x in data):

math.isnan(pd.NA) raises a TypeError instead of returning False. Only None and
float NaN are accepted as missing values.

Table.from_pandas does not raise for the same input, because its version of the
check uses x is np.nan. However, it does not recognize pd.NA as missing either. The
column stays a MaskedColumn of dtype object, with pd.NA stored under the mask. The
same data with np.nan for the missing value becomes a <U1 column.

This came up while reviewing #20599, which moves the check into the
_get_string_fill_value helper but keeps the same behavior.

Expected behavior

pd.NA in a string-like column should be treated as a missing value, the same way as
None and NaN. Both Table.from_df and Table.from_pandas should then return a
MaskedColumn with a native string dtype (<U1 in the example below) and that entry
masked.

At a minimum, Table.from_df should not raise. If a column can't be converted, it
could fall back to an object column, as from_pandas does.

How to Reproduce

import numpy as np
import pandas as pd
from astropy.table import Table

df = pd.DataFrame({"s": ["a", None]}, dtype="string")  # None is stored as pd.NA

Table.from_df(df)
# TypeError: must be real number, not NAType

t = Table.from_pandas(df)
t["s"].dtype, t["s"].mask.tolist()
# (dtype('O'), [False, True])   -- pd.NA kept in an object column

# For comparison, np.nan as the missing value works with both methods:
df2 = pd.DataFrame({"s": ["a", np.nan]}, dtype=object)
Table.from_df(df2)["s"].dtype      # dtype('<U1')
Table.from_pandas(df2)["s"].dtype  # dtype('<U1')

Results for other ways of holding the missing value:

DataFrame column from_df from_pandas
dtype="string" with None (becomes pd.NA) TypeError object column
dtype="string[pyarrow]" with None TypeError object column
dtype=object with pd.NA TypeError object column
dtype=object with None <U1 column object column
dtype=object with np.nan <U1 column <U1 column

Full traceback for Table.from_df(df):

Traceback (most recent call last):
  File "astropy/table/table.py", line 4368, in from_df
    return from_df(cls, df, index=index, units=units)
  File "astropy/table/_dataframes.py", line 391, in from_df
    if all((x is None) or isinstance(x, ts) or math.isnan(x) for x in data):
  File "astropy/table/_dataframes.py", line 391, in <genexpr>
    if all((x is None) or isinstance(x, ts) or math.isnan(x) for x in data):
                                               ~~~~~~~~~~^^^
TypeError: must be real number, not NAType

Versions

import astropy
astropy.system_info()
platform
--------
platform.platform() = 'macOS-26.6.2-arm64-arm-64bit-Mach-O'
platform.version() = 'Darwin Kernel Version 25.6.0: Fri Jul 31 19:18:48 PDT 2026; root:xnu-12377.161.14~5/RELEASE_ARM64_T6020'
platform.python_version() = '3.13.2'

packages
--------
astropy              8.1.0.dev423+g1d2de44a7
numpy                2.2.3
scipy                1.15.1
matplotlib           3.10.0
pandas               2.3.3
pyerfa               2.0.1.5

(The astropy line is from the editable install metadata; the source tree was main at
984804c.)

Activity

  1. codeNstruct commented on Oct 30, 2026

    @codeNstruct

    Hi! I'd like to work on this issue as my first code contribution to Astropy. I understand that the problem occurs when Table.from_df() processes pandas string columns containing pd.NA. I'd like to investigate the missing-value handling and add regression tests along with a fix. Is this issue still available for contribution? Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions