Skip to content

Commit 350be02

Browse files
committed
BUG:pandas-dev#29928 Fix to_json output with 'table' orient for single level MultiIndex.
Index field name in written json was incorrect, so applying read_json resulted in NaN index values. Dataframe to_json with 'table' orient now treats single level MultiIndex like single Index.
1 parent 054c49c commit 350be02

File tree

3 files changed

+26
-3
lines changed

3 files changed

+26
-3
lines changed

doc/source/whatsnew/v1.2.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ I/O
328328
- Bug in :func:`LongTableBuilder.middle_separator` was duplicating LaTeX longtable entires in the List of Tables of a LaTeX document (:issue:`34360`)
329329
- Bug in :meth:`read_csv` with ``engine='python'`` truncating data if multiple items present in first row and first element started with BOM (:issue:`36343`)
330330
- Removed ``private_key`` and ``verbose`` from :func:`read_gbq` as they are no longer supported in ``pandas-gbq`` (:issue:`34654`, :issue:`30200`)
331+
- Bug in :meth:`~DataFrame.to_json` with 'table' orient was writting wrong index field name for MultiIndex Dataframe with a single level (:issue:`29928`)
331332

332333
Plotting
333334
^^^^^^^^

pandas/io/json/_json.py

+8-3
Original file line numberDiff line numberDiff line change
@@ -298,8 +298,6 @@ def __init__(
298298
)
299299
raise ValueError(msg)
300300

301-
self.schema = build_table_schema(obj, index=self.index)
302-
303301
# NotImplemented on a column MultiIndex
304302
if obj.ndim == 2 and isinstance(obj.columns, MultiIndex):
305303
raise NotImplementedError("orient='table' is not supported for MultiIndex")
@@ -314,10 +312,17 @@ def __init__(
314312
raise ValueError(msg)
315313

316314
obj = obj.copy()
315+
316+
# Convert DataFrame to handled types before serializing
317+
if obj.index.nlevels == 1 and isinstance(obj.index, MultiIndex):
318+
obj.index = obj.index.get_level_values(0)
319+
320+
self.schema = build_table_schema(obj, index=self.index)
321+
317322
timedeltas = obj.select_dtypes(include=["timedelta"]).columns
318323
if len(timedeltas):
319324
obj[timedeltas] = obj[timedeltas].applymap(lambda x: x.isoformat())
320-
# Convert PeriodIndex to datetimes before serializing
325+
321326
if is_period_dtype(obj.index.dtype):
322327
obj.index = obj.index.to_timestamp()
323328

pandas/tests/io/json/test_json_table_schema.py

+17
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,23 @@ def test_to_json_categorical_index(self):
435435

436436
assert result == expected
437437

438+
@pytest.mark.parametrize("name", [None, "foo"])
439+
def test_multiindex_single_level(self, name):
440+
# GH29928
441+
index = pd.Index([1, 2, 3, 4], name=name)
442+
expected = pd.DataFrame(
443+
data=[[1, 1], [2, 2], [3, 3], [4, 4]], columns=["A", "B"], index=index
444+
)
445+
446+
index = pd.MultiIndex.from_tuples([(1,), (2,), (3,), (4,)], names=[name])
447+
df = pd.DataFrame(
448+
data=[[1, 1], [2, 2], [3, 3], [4, 4]], columns=["A", "B"], index=index
449+
)
450+
js = df.to_json(orient="table")
451+
result = pd.read_json(js, orient="table")
452+
453+
tm.assert_frame_equal(result, expected)
454+
438455
@pytest.mark.filterwarnings(
439456
"ignore:an integer is required (got type float)*:DeprecationWarning"
440457
)

0 commit comments

Comments
 (0)