如何在 pandas 中使用 apply 函数来实现这个 iterrow 案例?

Posted

技术标签:

【中文标题】如何在 pandas 中使用 apply 函数来实现这个 iterrow 案例?【英文标题】:How to implement this iterrow case using an apply function in pandas? 【发布时间】:2019-03-02 08:19:10 【问题描述】:

我有以下获取IP信息的代码:

import requests
import json
import pandas as pd
import swifter  

def get_ip(ip):
    response = requests.get ("http://ip-api.com/json/" + ip.rstrip())
    geo = response.json()
    location = 'lat': geo.get('lat', ''),
                'lon': geo.get('lon', ''),
                'region': geo.get('regionName', ''),
                'city': geo.get('city', ''),
                'org': geo.get('org', ''),
                'country': geo.get('countryCode', ''),
                'query': geo.get('query', '')
                
    return(location)

为了将其应用于整个 IP 数据帧 (df),我使用的是下一个:

df=pd.DataFrame(['85.56.19.4','188.85.165.103','81.61.223.131'])

for lab,row in df.iterrows():
    dip = get_ip(df.iloc[lab][0])
    try:
        ip.append(dip["query"])
        private.append('no')
        country.append(dip["country"])
        city.append(dip["city"])
        region.append(dip["region"])
        organization.append(dip["org"])
        latitude.append(dip["lat"])
        longitude.append(dip["lon"])
    except:
        ip.append(df.iloc[lab][0])
        private.append("yes")

但是,由于 iterrows 非常慢并且我需要更高的性能,所以我想使用 swiftapply,它是 apply 函数的扩展。我用过这个:

def ip(x):
    dip = get_ip(x)
    if (dip['ip']=='private')==True:
        ip.append(x)
        private.append("yes")
    else:
        ip.append(dip["ip"])
        private.append('no')
        country.append(dip["country"])
        city.append(dip["city"])
        region.append(dip["region"])
        organization.append(dip["org"])
        latitude.append(dip["lat"])
        longitude.append(dip["lon"])

df.swifter.apply(ip)

我收到以下错误: AttributeError: ("'Series' 对象没有属性 'rstrip'", '发生在索引 0')

我该如何解决?

【问题讨论】:

rstrip() 是一个仅适用于字符串的函数。看来您正在将它与非字符串对象一起使用,但我不确定在哪里(ip.rstrip() 是唯一出现的 rstrip()ip 可能是字符串) 【参考方案1】:

rstrip 是一个字符串操作。要将字符串操作应用于系列 Series,您必须首先在系列上调用 str 函数,这允许在 Series 上执行矢量化字符串操作。

具体来说,在您的代码中将ip.rstrip() 更改为ip.str.rstrip() 应该会解析您的AttributeError

经过一番挖掘,发现您尝试执行的requests.get 操作无法在pandas 内进行矢量化(请参阅Using Python Requests for several URLS in a dataframe)。我破解了以下内容,应该比使用iterrows 更有效率。下面的工作是利用np.vectorize 运行函数来获取每个IP 地址的信息。位置输入保存为新 DataFrame 中的新列。

首先,我更改了您的 get_ip 函数以返回 location 字典,而不是 (location)

接下来,我使用np.vectorize创建了一个矢量化函数:

vec_func = np.vectorize(lambda url: get_ip(url))

最后,vec_func 应用于 df 以创建一个新的 DataFrame,它将df 与来自vec_func 的位置输出合并,其中df[0] 是包含您的网址的列:

new_df = pd.concat([df, pd.DataFrame(vec_func(df[0]), columns=["response"])["response"].apply(pd.Series)], axis=1)

上面的代码以字典的形式为 DataFrame 中的每一行检索 API 响应,然后将字典映射到 DataFrame 中的列。最后,您的新 DataFrame 将如下所示:

                0      lat     lon     region      city             org country           query
0      85.56.19.4  37.3824 -5.9761  Andalusia   Seville   Orange Espana      ES      85.56.19.4
1  188.85.165.103  41.6561 -0.8773     Aragon  Zaragoza  Vodafone Spain      ES  188.85.165.103
2   81.61.223.131  40.3272 -3.7635     Madrid   Leganés    Vodafone Ono      ES   81.61.223.131

希望这可以解决 InvalidSchema 错误并让您获得比 iterrows() 更好的性能。

【讨论】:

更改它我得到另一个错误:InvalidSchema: ("No connection adapters were found for 'ip_address ipapi..., dtype: object'", '发生在索引 0') 我已按照此 [链接] (***.com/questions/15115328/…) 上的解决方案进行操作,但仍然出现错误。 您能否提供导致此新错误的完整 URL? 当然。 ("没有为 'ip_address h ttp://ip-api.com/json/85.56.19.4\nName: 0, dtype: object'找到连接适配器'", '发生在索引 0' ) PS:我在 h 和 t 之间放了一个空格,因为没有得到链接 @JavierLópezTomás- 我已经用InvalidSchema 错误的解决方法更新了答案。希望这会有所帮助。研究这个我学到了很多! 感谢您的回答。我已经测量了我的笔记本电脑和服务器中 100 个 IP 地址的数据帧的时间。在我的笔记本电脑中,您提出的方法比 iterrows 慢 1 秒(1 分钟对 1 分钟 1 秒),而在服务器中,它慢 0.4 秒(49.7 秒对 49.3 秒)

以上是关于如何在 pandas 中使用 apply 函数来实现这个 iterrow 案例?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 pandas 中使用 apply 函数,同时将索引映射到两个不同的数据帧?

Python Pandas:如何在不编写辅助函数的情况下使用 apply 广播操作

如何使用天数作为 pandas rolling_apply 函数的窗口

pandas使用apply函数:在dataframe数据列(column)上施加(apply)函数

pandas使用apply函数:在dataframe数据行(row)上施加(apply)函数

在Pandas中使用Apply Lambda函数具有多个if语句