From 7be068b961c02a736d16d4fe0b2eeb38c4f01309 Mon Sep 17 00:00:00 2001 From: fjdiod Date: Mon, 9 Jul 2018 15:17:17 +0300 Subject: [PATCH 1/4] ENH: Add Timedelta Support to JSON Reader with orient=table (#21140) --- pandas/io/json/table_schema.py | 11 +++++------ pandas/tests/io/json/test_json_table_schema.py | 6 +++--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/pandas/io/json/table_schema.py b/pandas/io/json/table_schema.py index 2dc176648fb31..5e4cecec5e3ec 100644 --- a/pandas/io/json/table_schema.py +++ b/pandas/io/json/table_schema.py @@ -6,7 +6,7 @@ import warnings import pandas._libs.json as json -from pandas import DataFrame +from pandas import DataFrame, Timedelta from pandas.api.types import CategoricalDtype import pandas.core.common as com from pandas.core.dtypes.common import ( @@ -163,7 +163,7 @@ def convert_json_field_to_pandas_type(field): elif typ == 'boolean': return 'bool' elif typ == 'duration': - return 'timedelta64' + return 'timedelta64[ns]' elif typ == 'datetime': if field.get('tz'): return 'datetime64[ns, {tz}]'.format(tz=field['tz']) @@ -306,10 +306,9 @@ def parse_table_schema(json, precise_float): raise NotImplementedError('table="orient" can not yet read timezone ' 'data') - # No ISO constructor for Timedelta as of yet, so need to raise - if 'timedelta64' in dtypes.values(): - raise NotImplementedError('table="orient" can not yet read ' - 'ISO-formatted Timedelta data') + for col, dtype in dtypes.items(): + if dtype == 'timedelta64[ns]': + df[col] = df[col].apply(Timedelta) df = df.astype(dtypes) diff --git a/pandas/tests/io/json/test_json_table_schema.py b/pandas/tests/io/json/test_json_table_schema.py index b6483d0e978ba..10ce834b08873 100644 --- a/pandas/tests/io/json/test_json_table_schema.py +++ b/pandas/tests/io/json/test_json_table_schema.py @@ -390,7 +390,7 @@ def test_convert_pandas_type_to_json_field_categorical(self, kind, ({'type': 'integer'}, 'int64'), ({'type': 'number'}, 'float64'), ({'type': 'boolean'}, 'bool'), - ({'type': 'duration'}, 'timedelta64'), + ({'type': 'duration'}, 'timedelta64[ns]'), ({'type': 'datetime'}, 'datetime64[ns]'), ({'type': 'datetime', 'tz': 'US/Hawaii'}, 'datetime64[ns, US/Hawaii]'), ({'type': 'any'}, 'object'), @@ -499,6 +499,7 @@ class TestTableOrientReader(object): 'level_0']) @pytest.mark.parametrize("vals", [ {'ints': [1, 2, 3, 4]}, + {'timedeltas': pd.timedelta_range('1H', periods=4, freq='T')}, {'objects': ['a', 'b', 'c', 'd']}, {'date_ranges': pd.date_range('2016-01-01', freq='d', periods=4)}, {'categoricals': pd.Series(pd.Categorical(['a', 'b', 'c', 'c']))}, @@ -516,7 +517,6 @@ def test_read_json_table_orient(self, index_nm, vals, recwarn): @pytest.mark.parametrize("index_nm", [ None, "idx", "index"]) @pytest.mark.parametrize("vals", [ - {'timedeltas': pd.timedelta_range('1H', periods=4, freq='T')}, {'timezones': pd.date_range('2016-01-01', freq='d', periods=4, tz='US/Central')}]) def test_read_json_table_orient_raises(self, index_nm, vals, recwarn): @@ -530,7 +530,7 @@ def test_comprehensive(self): {'A': [1, 2, 3, 4], 'B': ['a', 'b', 'c', 'c'], 'C': pd.date_range('2016-01-01', freq='d', periods=4), - # 'D': pd.timedelta_range('1H', periods=4, freq='T'), + 'D': pd.timedelta_range('1H', periods=4, freq='T'), 'E': pd.Series(pd.Categorical(['a', 'b', 'c', 'c'])), 'F': pd.Series(pd.Categorical(['a', 'b', 'c', 'c'], ordered=True)), From 7f1336c7aa7f8f98777764713c0c952c1686969b Mon Sep 17 00:00:00 2001 From: fjdiod Date: Fri, 20 Jul 2018 20:40:37 +0300 Subject: [PATCH 2/4] add whatsnew entry --- doc/source/whatsnew/v0.24.0.txt | 1 + pandas/io/json/table_schema.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v0.24.0.txt b/doc/source/whatsnew/v0.24.0.txt index a1b47e1601551..fc884c76da6dd 100644 --- a/doc/source/whatsnew/v0.24.0.txt +++ b/doc/source/whatsnew/v0.24.0.txt @@ -121,6 +121,7 @@ Other Enhancements - :class:`IntervalIndex` has gained the :meth:`~IntervalIndex.set_closed` method to change the existing ``closed`` value (:issue:`21670`) - :func:`~DataFrame.to_csv` and :func:`~DataFrame.to_json` now support ``compression='infer'`` to infer compression based on filename (:issue:`15008`) - :func:`to_timedelta` now supports iso-formated timedelta strings (:issue:`21877`) +- :func:`read_json` now parse timedelta with `orient='table'` (:issue:`21140`) - .. _whatsnew_0240.api_breaking: diff --git a/pandas/io/json/table_schema.py b/pandas/io/json/table_schema.py index 5e4cecec5e3ec..78ce7c7a00224 100644 --- a/pandas/io/json/table_schema.py +++ b/pandas/io/json/table_schema.py @@ -6,7 +6,7 @@ import warnings import pandas._libs.json as json -from pandas import DataFrame, Timedelta +from pandas import DataFrame, to_timedelta from pandas.api.types import CategoricalDtype import pandas.core.common as com from pandas.core.dtypes.common import ( @@ -308,7 +308,7 @@ def parse_table_schema(json, precise_float): for col, dtype in dtypes.items(): if dtype == 'timedelta64[ns]': - df[col] = df[col].apply(Timedelta) + df[col] = to_timedelta(df[col]) df = df.astype(dtypes) From b0150e45eb2237b715486adfec42405258f3ef22 Mon Sep 17 00:00:00 2001 From: fjdiod Date: Mon, 9 Jul 2018 15:17:17 +0300 Subject: [PATCH 3/4] ENH: Add Timedelta Support to JSON Reader with orient=table (#21140) add iso-format support to to_timedelta Revert "add iso-format support to to_timedelta" This reverts commit 3f5f1769a0a780bbad6e99ef1e0cfe2d2e00c6bc. --- pandas/io/json/table_schema.py | 11 +++++------ pandas/tests/io/json/test_json_table_schema.py | 6 +++--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/pandas/io/json/table_schema.py b/pandas/io/json/table_schema.py index 2dc176648fb31..5e4cecec5e3ec 100644 --- a/pandas/io/json/table_schema.py +++ b/pandas/io/json/table_schema.py @@ -6,7 +6,7 @@ import warnings import pandas._libs.json as json -from pandas import DataFrame +from pandas import DataFrame, Timedelta from pandas.api.types import CategoricalDtype import pandas.core.common as com from pandas.core.dtypes.common import ( @@ -163,7 +163,7 @@ def convert_json_field_to_pandas_type(field): elif typ == 'boolean': return 'bool' elif typ == 'duration': - return 'timedelta64' + return 'timedelta64[ns]' elif typ == 'datetime': if field.get('tz'): return 'datetime64[ns, {tz}]'.format(tz=field['tz']) @@ -306,10 +306,9 @@ def parse_table_schema(json, precise_float): raise NotImplementedError('table="orient" can not yet read timezone ' 'data') - # No ISO constructor for Timedelta as of yet, so need to raise - if 'timedelta64' in dtypes.values(): - raise NotImplementedError('table="orient" can not yet read ' - 'ISO-formatted Timedelta data') + for col, dtype in dtypes.items(): + if dtype == 'timedelta64[ns]': + df[col] = df[col].apply(Timedelta) df = df.astype(dtypes) diff --git a/pandas/tests/io/json/test_json_table_schema.py b/pandas/tests/io/json/test_json_table_schema.py index b6483d0e978ba..10ce834b08873 100644 --- a/pandas/tests/io/json/test_json_table_schema.py +++ b/pandas/tests/io/json/test_json_table_schema.py @@ -390,7 +390,7 @@ def test_convert_pandas_type_to_json_field_categorical(self, kind, ({'type': 'integer'}, 'int64'), ({'type': 'number'}, 'float64'), ({'type': 'boolean'}, 'bool'), - ({'type': 'duration'}, 'timedelta64'), + ({'type': 'duration'}, 'timedelta64[ns]'), ({'type': 'datetime'}, 'datetime64[ns]'), ({'type': 'datetime', 'tz': 'US/Hawaii'}, 'datetime64[ns, US/Hawaii]'), ({'type': 'any'}, 'object'), @@ -499,6 +499,7 @@ class TestTableOrientReader(object): 'level_0']) @pytest.mark.parametrize("vals", [ {'ints': [1, 2, 3, 4]}, + {'timedeltas': pd.timedelta_range('1H', periods=4, freq='T')}, {'objects': ['a', 'b', 'c', 'd']}, {'date_ranges': pd.date_range('2016-01-01', freq='d', periods=4)}, {'categoricals': pd.Series(pd.Categorical(['a', 'b', 'c', 'c']))}, @@ -516,7 +517,6 @@ def test_read_json_table_orient(self, index_nm, vals, recwarn): @pytest.mark.parametrize("index_nm", [ None, "idx", "index"]) @pytest.mark.parametrize("vals", [ - {'timedeltas': pd.timedelta_range('1H', periods=4, freq='T')}, {'timezones': pd.date_range('2016-01-01', freq='d', periods=4, tz='US/Central')}]) def test_read_json_table_orient_raises(self, index_nm, vals, recwarn): @@ -530,7 +530,7 @@ def test_comprehensive(self): {'A': [1, 2, 3, 4], 'B': ['a', 'b', 'c', 'c'], 'C': pd.date_range('2016-01-01', freq='d', periods=4), - # 'D': pd.timedelta_range('1H', periods=4, freq='T'), + 'D': pd.timedelta_range('1H', periods=4, freq='T'), 'E': pd.Series(pd.Categorical(['a', 'b', 'c', 'c'])), 'F': pd.Series(pd.Categorical(['a', 'b', 'c', 'c'], ordered=True)), From 68425829a100d4fb4e31baa3e079f14dab032be5 Mon Sep 17 00:00:00 2001 From: fjdiod Date: Thu, 26 Jul 2018 19:02:21 +0300 Subject: [PATCH 4/4] fix typo --- doc/source/whatsnew/v0.24.0.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.24.0.txt b/doc/source/whatsnew/v0.24.0.txt index fc884c76da6dd..9bd026e5a969c 100644 --- a/doc/source/whatsnew/v0.24.0.txt +++ b/doc/source/whatsnew/v0.24.0.txt @@ -121,7 +121,7 @@ Other Enhancements - :class:`IntervalIndex` has gained the :meth:`~IntervalIndex.set_closed` method to change the existing ``closed`` value (:issue:`21670`) - :func:`~DataFrame.to_csv` and :func:`~DataFrame.to_json` now support ``compression='infer'`` to infer compression based on filename (:issue:`15008`) - :func:`to_timedelta` now supports iso-formated timedelta strings (:issue:`21877`) -- :func:`read_json` now parse timedelta with `orient='table'` (:issue:`21140`) +- :func:`read_json` now parses timedelta with `orient='table'` (:issue:`21140`) - .. _whatsnew_0240.api_breaking: