Python Pandas中的'apply'函数返回一个Series,而无法转换为DataFrame。

6
好的,我有些不知所措。我正在使用geopy对数据框进行地理编码。我编写了一个简单的函数来接受输入 - 国家名称 - 并返回纬度和经度。我使用apply运行函数并返回Pandas系列对象。但我似乎无法将其转换为数据框。我相信我错过了一些显而易见的东西,但我是Python新手,仍在阅读官方文档。顺便说一下,地理编码器函数非常好用。
# Import libraries 
import os 
import pandas as pd 
import numpy as np
from geopy.geocoders import Nominatim

def locate(x):
    geolocator = Nominatim()
    # print(x) # debug
    try:
        #Get geocode
        location = geolocator.geocode(x, timeout=8, exactly_one=True)
        lat = location.latitude
        lon = location.longitude
    except:
        #didn't work for some reason that I really don't care about
        lat = np.nan
        lon = np.nan
   #  print(lat,lon) #debug
    return lat,  lon # Note: also tried return { 'LAT': lat, 'LON': lon }

df_geo_in = df_addr.drop_duplicates(['COUNTRY']).reset_index()    #works perfectly
df_geo_in['LAT'], df_geo_in['LON']  = df_geo_in.applymap(locate) 
# error: returns more than 2 values - default index + column with results

我也试过。
df_geo_in['LAT','LON'] = df_geo_in.applymap(locate)

我得到了一个没有索引的单个数据框,其中只有一个列,其中包含系列。
我尝试了许多其他方法,包括“applymap”:
source_cols = ['LAT','LON'] 
new_cols = [str(x) for x in source_cols]

df_geo_in = df_addr.drop_duplicates(['COUNTRY']).set_index(['COUNTRY']) 
df_geo_in[new_cols] = df_geo_in.applymap(locate)

长时间后返回了一个错误:

ValueError: 列必须与键的长度相同

我还尝试使用 df.from_dict(df_geo_in) 方法手动将系列转换为数据帧,但没有成功。

目标是对 166 个独特的国家进行地理编码,然后将其与 df_addr 中的 188K 地址连接起来。如果可能的话,我想在代码中使用 pandas,而不编写循环。但我还没有找到将系列转换为数据帧的方法,这是我第一次尝试使用 apply。

提前感谢 - 古老的 C 程序员


在不同的点上,type(df_geo_in)会返回什么?它是一个数据框还是一个序列?无论如何,您可能需要使用df_geo_in.fromdict()。如果没有定义df,则df没有意义。您可能需要pd.DataFrame(df_geo_in)。 - dartdog
2个回答

7
我假设df_geo是只有一列的数据框,因此我认为以下代码应该有效:

更改为:

return lat,  lon

to

return pd.Series([lat,  lon])

那么你应该能够像这样分配:

df_geo_in[['LAT', 'LON']] = df_geo_in.apply(locate)

你尝试做的是将applymap的结果分配给两个新列,这在这里是不正确的,因为applymap是设计用于对df中的每个元素进行操作的,所以除非lhs具有相同的预期形状,否则这不会产生预期的结果。
你后来的方法也是不正确的,因为你删除了重复的国家,然后希望将每个国家的地理位置重新分配回去,但形状不同。
对于大型的df,创建不重复的地理位置df,然后将其与较大的df合并可能更快,如下所示:
geo_lookup = df_addr.drop_duplicates(['COUNTRY'])
geo_lookup[['LAT','LNG']] = geo_lookup['COUNTRY'].apply(locate)
df_geo_in.merge(geo_lookup, left_on='COUNTRY', right_on='COUNTRY', how='left')

这将创建一个包含不重复国家和地理位置地址的数据框,然后我们执行左连接回到主数据框。


谢谢!问题解决了!现在我只需要更好地理解为什么。 - Harvey

0

通常使用一些示例数据进行测试会更容易,但请尝试以下zip函数以查看其是否有效。

df_geo_in['LAT_LON'] = df_geo_in.applymap(locate) 
df_geo_in['LAT'], df_geo_in['LON'] = zip(*df_geo_in.LAT_LON)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接