Pandas 在 basemap/proj 地图绘制时出错

2 投票
1 回答
1859 浏览
提问于 2025-04-18 03:08

我运行了下面这段Python代码,这段代码是书籍《Python数据分析》中关于“绘制地图:可视化海地地震危机数据”的一个例子。具体在第242到246页。

这段代码本来是要生成一张海地的地图,但我遇到了下面的错误:

Traceback (most recent call last):
  File "Haiti.py", line 74, in <module>
    x, y = m(cat_data.LONGITUDE, cat_data.LATITUDE)
  File "/usr/local/lib/python2.7/site-packages/mpl_toolkits/basemap/__init__.py", line 1148, in __call__
    xout,yout = self.projtran(x,y,inverse=inverse)
  File "/usr/local/lib/python2.7/site-packages/mpl_toolkits/basemap/proj.py", line 286, in __call__
    outx,outy = self._proj4(x, y, inverse=inverse)
  File "/usr/local/lib/python2.7/site-packages/mpl_toolkits/basemap/pyproj.py", line 388, in __call__
    _proj.Proj._fwd(self, inx, iny, radians=radians, errcheck=errcheck)
  File "_proj.pyx", line 122, in _proj.Proj._fwd (src/_proj.c:1571)
RuntimeError

我检查了一下我的电脑上是否安装了mpl_toolkits.basemapproj模块。这两个模块的安装都按照说明进行了,Basemap是从源代码安装的,而proj是通过Homebrew安装的,看起来都没问题。

如果你已经安装了basemap和proj,这段代码能顺利运行吗?如果不能,你觉得是模块安装的问题,代码本身的问题,还是其他什么原因呢?

Haiti.csv文件可以从这个链接下载。

import pandas as pd
import numpy as np
from pandas import DataFrame

data = pd.read_csv('Haiti.csv')

data = data[(data.LATITUDE > 18) & (data.LATITUDE < 20) &
        (data.LONGITUDE > -75) & (data.LONGITUDE < -70)
        & data.CATEGORY.notnull()]

def to_cat_list(catstr):
    stripped = (x.strip() for x in catstr.split(','))
    return [x for x in stripped if x]

def get_all_categories(cat_series):
    cat_sets = (set(to_cat_list(x)) for x in cat_series) 
    return sorted(set.union(*cat_sets))

def get_english(cat):
    code, names = cat.split('.') 
    if '|' in names:
        names = names.split(' | ')[1] 
    return code, names.strip()

all_cats = get_all_categories(data.CATEGORY)
english_mapping = dict(get_english(x) for x in all_cats)

def get_code(seq):
    return [x.split('.')[0] for x in seq if x]

all_codes = get_code(all_cats)
code_index = pd.Index(np.unique(all_codes))
dummy_frame = DataFrame(np.zeros((len(data), len(code_index))),
                        index=data.index, columns=code_index)

for row, cat in zip(data.index, data.CATEGORY): 
    codes = get_code(to_cat_list(cat)) 
    dummy_frame.ix[row, codes] = 1

data = data.join(dummy_frame.add_prefix('category_'))

from mpl_toolkits.basemap import Basemap 
import matplotlib.pyplot as plt

def basic_haiti_map(ax=None, lllat=17.25, urlat=20.25, lllon=-75, urlon=-71):
    # create polar stereographic Basemap instance. 
    m = Basemap(ax=ax, projection='stere', 
                lon_0=(urlon + lllon) / 2, 
                lat_0=(urlat + lllat) / 2,
                llcrnrlat=lllat, urcrnrlat=urlat, 
                llcrnrlon=lllon, urcrnrlon=urlon, 
                resolution='f')
    # draw coastlines, state and country boundaries, edge of map. m.drawcoastlines()
    m.drawstates()
    m.drawcountries()
    return m

fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(12, 10)) 
fig.subplots_adjust(hspace=0.05, wspace=0.05)

to_plot = ['2a', '1', '3c', '7a']

lllat=17.25; urlat=20.25; lllon=-75; urlon=-71

for code, ax in zip(to_plot, axes.flat):
    m = basic_haiti_map(ax, lllat=lllat, urlat=urlat,
                        lllon=lllon, urlon=urlon) 

    cat_data = data[data['category_%s' % code] == 1]

    # compute map proj coordinates.
    print cat_data.LONGITUDE, cat_data.LATITUDE
    x, y = m(cat_data.LONGITUDE, cat_data.LATITUDE)

    m.plot(x, y, 'k.', alpha=0.5)
    ax.set_title('%s: %s' % (code, english_mapping[code]))

1 个回答

5

这个问题的解决方法是把 m(cat_data.LONGITUDE, cat_data.LATITUDE) 改成 m(cat_data.LONGITUDE.values, cat_data.LATITUDE.values)。感谢 Alex Messina 的发现

经过我进一步的研究,发现从2013年12月31日发布的v0.13.0版本开始,pandas改变了处理DataFrame(源自NDFrame)中的Series数据的方式,应该用 .values 来传递给像 basemap/proj 这样的Cython函数。

以下是来自 pandas的github提交日志的引用:

+.. warning::
 +
 +   In 0.13.0 since ``Series`` has internaly been refactored to no longer sub-class ``ndarray``
 +   but instead subclass ``NDFrame``, you can **not pass** a ``Series`` directly as a ``ndarray`` typed parameter
 +   to a cython function. Instead pass the actual ``ndarray`` using the ``.values`` attribute of the Series.
 +
 +   Prior to 0.13.0
 +
 +   .. code-block:: python
 +
 +        apply_integrate_f(df['a'], df['b'], df['N'])
 +
 +   Use ``.values`` to get the underlying ``ndarray``
 +
 +   .. code-block:: python
 +
 +        apply_integrate_f(df['a'].values, df['b'].values, df['N'].values)

你可以在 这里 找到修正后的示例代码。

撰写回答