Skip to content

Table.from_df and Table.from_pandas give different results #20807

Open

Description

@taldcroft

Description

Table.from_df (narwhals) and Table.from_pandas are documented as two routes for the same conversion, but for several common pandas column types they give different tables for the same DataFrame. #20805 fixes the differences for string-like columns with missing values and this issue collects the ones that remain. Each case below is a plain pd.DataFrame with one column; there is no mixin or unit involved.

Column from_df from_pandas Which looks right
boolean with pd.NA, or bool[pyarrow] with null MaskedColumn object MaskedColumn bool from_pandas. from_df gives object for a polars Boolean with null too, so this is a gap in from_df, not pandas-specific.
Categorical(["a", None]) MaskedColumn object MaskedColumn <U1 from_pandas. Same for polars Categorical and Enum.
datetime64[ns, US/Eastern] with NaT Time (UTC, correct instants, NaT masked) MaskedColumn object of Timestamps from_df. from_pandas only handles naive datetime64.
index=True with an unnamed index KeyError: 'Requested level (index) does not match index name (None)' ['index', 'x'] from_pandas. from_df passes the fallback name to reset_index, which expects the real level name.

With these fixed, the two methods would agree on every one of the 23 common pandas column types I tried (numeric and nullable numeric, pyarrow-backed numeric, naive datetime and timedelta with and without missing values, string, object with mixed types, lists, dicts, complex).

Raised by @cgobat on #20805, who had assumed the two methods were interchangeable for pandas input. Whether from_pandas should eventually become a wrapper around from_df is a separate question; this issue is only about making them agree.

How to Reproduce

Run on #20805 (the string cases are already fixed there):

import pandas as pd
from astropy.table import Table

cases = {
    "boolean": pd.Series([True, None], dtype="boolean"),
    "bool[pyarrow]": pd.Series([True, None], dtype="bool[pyarrow]"),
    "category": pd.Categorical(["a", None]),
    "datetime64[ns, US/Eastern]": pd.Series(pd.to_datetime(["2020-01-01", None])).dt.tz_localize("US/Eastern"),
}
for label, s in cases.items():
    df = pd.DataFrame({"c": s})
    print(f"\n# {label}")
    for m in ("from_df", "from_pandas"):
        c = getattr(Table, m)(df)["c"]
        print(f"{m:12s} {type(c).__name__:13s} {str(getattr(c, 'dtype', '')):8s} {list(c)}")

print("\n# index=True with an unnamed index")
df = pd.DataFrame({"x": [1, 2]})
for m in ("from_df", "from_pandas"):
    try:
        print(f"{m:12s} {getattr(Table, m)(df, index=True).colnames}")
    except Exception as e:
        print(f"{m:12s} {type(e).__name__}: {e}")
# boolean
from_df      MaskedColumn  object   [True, masked]
from_pandas  MaskedColumn  bool     [np.True_, masked]

# bool[pyarrow]
from_df      MaskedColumn  object   [True, masked]
from_pandas  MaskedColumn  bool     [np.True_, masked]

# category
from_df      MaskedColumn  object   ['a', masked]
from_pandas  MaskedColumn  <U1      [np.str_('a'), masked]

# datetime64[ns, US/Eastern]
from_df      Time          Time     [<Time object: scale='utc' format='isot' value=2020-01-01T05:00:00.000>, <Time object: scale='utc' format='isot' value=                    ———>]
from_pandas  MaskedColumn  object   [Timestamp('2020-01-01 00:00:00-0500', tz='US/Eastern'), masked]

# index=True with an unnamed index
from_df      KeyError: 'Requested level (index) does not match index name (None)'
from_pandas  ['index', 'x']

Versions

astropy 8.1.0.dev506 (branch of #20805 on main at 68e0005), pandas 2.3.3, narwhals 2.20.0, polars 1.32.3, numpy 2.2.3, Python 3.13, macOS arm64.

Activity

  1. changed the title [-]`Table.from_df` and `Table.from_pandas` give different results for nullable booleans, categoricals, tz-aware datetimes, and an unnamed index[/-] [+]`Table.from_df` and `Table.from_pandas` give different results[/+] on Nov 9, 2026
  2. gr-0x076 commented on Nov 9, 2026

    @gr-0x076
  3. self-assigned this
    on Nov 9, 2026
  4. taldcroft commented on Nov 9, 2026

    @taldcroft
    MemberAuthor

    @gr-0x076 - sorry, I've assigned myself, this issue is not available.

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