【问题标题】:Pandas to_json not outputting null for NaTPandas to_json 不为 NaT 输出 null
【发布时间】:2013-11-07 21:24:43
【问题描述】:

我正在使用 Pandas 0.12.0,在将系列或数据帧转换为 json 时,我看到一些与文档相矛盾的行为。

如果我创建一个包含几个包含空值的日期的系列,我会得到这样的结果:

>>> s = pandas.Series(data=[datetime.datetime.now(), datetime.datetime.now(), None])
>>> s
0    2013-11-07 16:10:47.530771
1    2013-11-07 16:10:47.530782
2                          None
dtype: object

根据http://pandas.pydata.org/pandas-docs/dev/io.html#writing-json,在转换为json时,None、NaT和NaN值应该输出为null。

如果我随后输出 to_json,我会得到第三个条目的空值,正如预期的那样。

>>> s.to_json()
'{"0":1383840647530771000,"1":1383840647530782000,"2":null}'

但是,对于其他一些计算,我需要确保数据类型是 datetime64[ns],因此我将字段转换为 Pandas 中的日期时间,如下所示:

>>> t = pandas.to_datetime(s)
>>> t
0   2013-11-07 16:10:47.530771
1   2013-11-07 16:10:47.530782
2                          NaT
dtype: datetime64[ns]

None 现在是 NaT,这是一致且符合预期的。然后我尝试再次输出 json,我得到 NaT 值的负值,而不是我期望的 null。

>>> t.to_json()
'{"0":1383840647530771000,"1":1383840647530782000,"2":-9223372036854775808}'

使用 iso 格式时会变得更糟,因为它会尝试格式化日期,但大多数解析器无法弄清楚如何处理输出日期,这会造成各种破坏。

>>> t.to_json(date_format='iso')
'{"0":"2013-11-07T16:10:47.530771","1":"2013-11-07T16:10:47.530782","2":"0001-255-255T00:00:00"}'

关于我应该如何在这里进行的任何想法?谢谢!

编辑

看起来这是 pandas.NaT 的字符串表示的问题?

>>> str(pandas.NaT)
'0001-255-255 00:00:00'

【问题讨论】:

  • FWIW 我无法在我今天早些时候拉的树干中重现这个;我分别得到'{"0":1383841824833,"1":1383841824833,"2":null}''{"0":"2013-11-07T16:30:24.833Z","1":"2013-11-07T16:30:24.833Z","2":null}'
  • iirc 这在 0.13 的早期开发中已修复。 0.13 rc 将于下周初发布/您可以尝试使用 master 来验证它是否已修复。或者,您可以在传递 to_json 之前进行字符串化
  • 确认这已在trunk/0.13中修复,谢谢大家。我在问题列表中进行了快速搜索,但没有遇到这个问题。 Jeff,在 0.13 发布之前,我会尝试你的 hacky 解决方法。

标签: python pandas


【解决方案1】:

有点hacky,但你可以这样做

 In [13]: s = Series(pd.to_datetime(['20130101',None]))

 In [14]: s
 0   2013-01-01 00:00:00 
 1                   NaT
 dtype: datetime64[ns]

 In [15]: def f(x):
             if isnull(x): 
                return 'null'
             return x.isoformat()    ....: 

 In [16]: s.apply(f).to_json() 

 Out[16]:
 '{"0":"2013-01-01T00:00:00","1":"null"}'

【讨论】:

  • 谢谢杰夫,这主要是有效的。我将其更改为return None,否则 json 具有字符串“null”而不是实际的 null 值。干杯!
【解决方案2】:

只需创建一个自定义编码器:

class CustomEncoder(json.JSONEncoder):
    def default(self, obj):
        if pd.isnull(obj):
            return None
        elif isinstance(obj, datetime):
            return obj.isoformat()
        elif isinstance(obj, date):
            return obj.isoformat()
        elif isinstance(obj, timedelta):
            return (datetime.min + obj).time().isoformat()
        else:
            return super(CustomEncoder, self).default(obj)

然后用它来编码一个数据帧:

df_as_dict = df.to_dict(outtype = 'records')  # transform to dict

df_as_json = CustomEncoder().encode(df_as_dict) #transform to json

由于编码器对数据进行了标准化,因此常规解码器可以很好地将其转换回数据帧:

result_as_dict = json.JSONDecoder().decode(df_as_json) # decode back to dict

result_as_df = pd.DataFrame(result_as_dict)  # transform dict back to dataframe

当然,如果您在编码之前将数据帧放入更大的字典中,这也将起作用,例如

input_dict = {'key_1':val_1,'key_2':val_2,...,'df_as_dict':df_as_dict}
input_json = CustomEncoder().encode(input_dict)
input_json_back_as_dict = json.JSONDecoder().decode(input_json)
input_df_back_as_dict = input_json_back_as_dict['df_as_dict']
input_df_back_as_df = pd.DataFrame(input_df_back_as_dict)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-10
    • 2017-08-06
    • 2014-11-06
    • 2018-05-19
    • 1970-01-01
    • 2015-07-25
    相关资源
    最近更新 更多