【问题标题】:'Expected String or Unicode' when reading JSON with Pandas使用 Pandas 读取 JSON 时出现“预期字符串或 Unicode”
【发布时间】:2014-09-10 23:22:09
【问题描述】:

我尝试读取一个Openstreetmaps API 输出JSON 字符串,这是有效的。

我正在使用以下代码:

import pandas as pd
import requests

# Links unten
minLat = 50.9549
minLon = 13.55232

# Rechts oben
maxLat = 51.1390
maxLon = 13.89873

osmrequest = {'data': '[out:json][timeout:25];(node["highway"="bus_stop"](%s,%s,%s,%s););out body;>;out skel qt;' % (minLat, minLon, maxLat, maxLon)}
osmurl = 'http://overpass-api.de/api/interpreter'
osm = requests.get(osmurl, params=osmrequest)

osmdata = osm.json()

osmdataframe = pd.read_json(osmdata)

引发以下错误:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-66-304b7fbfb645> in <module>()
----> 1 osmdataframe = pd.read_json(osmdata)

/Users/paul/anaconda/lib/python2.7/site-packages/pandas/io/json.pyc in read_json(path_or_buf, orient, typ, dtype, convert_axes, convert_dates, keep_default_dates, numpy, precise_float, date_unit)
    196         obj = FrameParser(json, orient, dtype, convert_axes, convert_dates,
    197                           keep_default_dates, numpy, precise_float,
--> 198                           date_unit).parse()
    199 
    200     if typ == 'series' or obj is None:

/Users/paul/anaconda/lib/python2.7/site-packages/pandas/io/json.pyc in parse(self)
    264 
    265         else:
--> 266             self._parse_no_numpy()
    267 
    268         if self.obj is None:

/Users/paul/anaconda/lib/python2.7/site-packages/pandas/io/json.pyc in _parse_no_numpy(self)
    481         if orient == "columns":
    482             self.obj = DataFrame(
--> 483                 loads(json, precise_float=self.precise_float), dtype=None)
    484         elif orient == "split":
    485             decoded = dict((str(k), v)

TypeError: Expected String or Unicode

如何修改请求或者Pandasread_json,避免报错?顺便问一下,有什么问题?

【问题讨论】:

    标签: python json pandas openstreetmap overpass-api


    【解决方案1】:

    如果将 json 字符串打印到文件中,

    content = osm.read()
    with open('/tmp/out', 'w') as f:
        f.write(content)
    

    你会看到这样的东西:

    {
      "version": 0.6,
      "generator": "Overpass API",
      "osm3s": {
        "timestamp_osm_base": "2014-07-20T07:52:02Z",
        "copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL."
      },
      "elements": [
    
    {
      "type": "node",
      "id": 536694,
      "lat": 50.9849256,
      "lon": 13.6821776,
      "tags": {
        "highway": "bus_stop",
        "name": "Niederhäslich Bergmannsweg"
      }
    },
    ...]}
    

    如果要将 JSON 字符串转换为 Python 对象,它将是一个字典,其 elements 键是字典列表。绝大多数数据都在这个字典列表中。

    此 JSON 字符串不能直接转换为 Pandas 对象。什么是索引,什么是列? 当然,您不希望 [u'elements', u'version', u'osm3s', u'generator'] 成为列,因为几乎所有信息都在 elements list-of-dicts 中。

    但如果您希望 DataFrame 仅包含 elements list-of-dicts 中的数据,那么您必须指定这一点,因为 Pandas 无法为您做出该假设。

    更复杂的事情是elements 中的每个字典都是嵌套字典。考虑elements 中的第一个字典:

    {
      "type": "node",
      "id": 536694,
      "lat": 50.9849256,
      "lon": 13.6821776,
      "tags": {
        "highway": "bus_stop",
        "name": "Niederhäslich Bergmannsweg"
      }
    }
    

    ['lat', 'lon', 'type', 'id', 'tags'] 应该是列吗? 这似乎是合理的,除了 tags 列最终会成为一列字典。这通常不是很有用。如果将tags dict 中的键做成列可能会更好。我们可以这样做,但我们必须自己编写代码,因为 Pandas 无法知道我们想要什么。


    import pandas as pd
    import requests
    # Links unten
    minLat = 50.9549
    minLon = 13.55232
    
    # Rechts oben
    maxLat = 51.1390
    maxLon = 13.89873
    
    osmrequest = {'data': '[out:json][timeout:25];(node["highway"="bus_stop"](%s,%s,%s,%s););out body;>;out skel qt;' % (minLat, minLon, maxLat, maxLon)}
    osmurl = 'http://overpass-api.de/api/interpreter'
    osm = requests.get(osmurl, params=osmrequest)
    
    osmdata = osm.json()
    osmdata = osmdata['elements']
    for dct in osmdata:
        for key, val in dct['tags'].iteritems():
            dct[key] = val
        del dct['tags']
    
    osmdataframe = pd.DataFrame(osmdata)
    print(osmdataframe[['lat', 'lon', 'name']].head())
    

    产量

             lat        lon                        name
    0  50.984926  13.682178  Niederhäslich Bergmannsweg
    1  51.123623  13.782789                Sagarder Weg
    2  51.065752  13.895734     Weißig, Einkaufszentrum
    3  51.007140  13.698498          Stuttgarter Straße
    4  51.010199  13.701411          Heilbronner Straße
    

    【讨论】:

    • 很好的解释!!我完全理解这一点,但无法为我自己解开这个谜。谢谢!在osmdata = json.loads(osm.read()) 行中可能不正确,因为我得到:AttributeError: 'Response' object has no attribute 'read'
    • 必须是osmdata = json.loads(osm.content)
    • @Balzer82:我的请求版本太旧了;从那时起,API 发生了变化。我已由requests 更新,并将编辑上面的代码。
    猜你喜欢
    • 2020-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-29
    • 1970-01-01
    相关资源
    最近更新 更多