Pandas 数据分析工具的使用技巧

Pandas是一个基于Python编程语言构建的开源数据分析工具,它以其易用性和灵活性而广受欢迎。使用Pandas已经有一段时间了,它总是以新特性、快捷方式和多种完成任务的方法给带来惊喜。然而,发现遵循一些学到的约定随着时间的推移是有益的。发现的一些最有用的特性包括'apply()'和'lambda()'。当遇到创建新列或过滤器的复杂逻辑时,就会转向apply和lambda。这种情况经常发生,当一个公司向提出特殊请求时。本文的目标是展示apply和lambda的强大之处。将使用过去10年IMDB上1000部热门电影的数据集。也可以在Kaggle上跟随操作。

导入必要的库

import numpy as np # 线性代数 import pandas as pd # pandas默认设置 pd.options.display.max_columns = 500 pd.options.display.max_rows = 500 import os

读取数据

首先,需要读取数据。这里使用Pandas的read_csv函数来读取IMDB电影数据。

df = pd.read_csv("IMDB-Movie-Data.csv") print(df.head()) # 打印前五行数据

接下来,可以重命名一些列,以便于后续的操作。

df.rename(columns={'Revenue (Millions)':'Rev_M','Runtime (Minutes)':'Runtime_min'},inplace=True)

创建新列

创建新列有多种方法,如果需要一个列是其他列的和或差,可以使用基本的算术运算。例如,可以使用IMDB评分和标准化Metascore来计算平均评分。

df['AvgRating'] = (df['Rating'] + df['Metascore']/10)/2

有时候,可能需要根据一些复杂的逻辑来创建新列。例如,想要根据多个标准创建一个自定义电影评分。假设想要增加惊悚片的IMDB评分,但只有当IMDB评分小于或等于10时。同样,如果电影是喜剧片,想要从排名中减去一分。如何实现这一点呢?在这种情况下,通常会使用apply/lambda。首先,将展示想要实现的目标。

def custom_rating(genre,rating): if 'Thriller' in genre: return min(10,rating+1) elif 'Comedy' in genre: return max(0,rating-1) else: return rating df['CustomRating'] = df.apply(lambda x: custom_rating(x['Genre'],x['Rating']),axis=1)

一般结构是:创建一个函数,它接受想要实验的列值,并创建逻辑。在这个案例中,只使用了genre和rating列。可以使用apply函数与lambda和axis=1。一般语法是:df.apply(lambda x: function (x[‘col1’],x[‘col2’]),axis=1)。因为只需要关心自定义函数,应该能够使用apply/lambda设计几乎任何逻辑。

过滤数据框

使用Pandas进行数据框的过滤和子集选择非常简单。可以使用正常的操作符和&、|操作符来过滤和子集数据框。

# 单条件:所有评分大于8的电影数据框 df_gt_8 = df[df['Rating']>8] df_gt_8.head() # 多条件:AND - 所有评分大于8且票数超过100000的电影 And_df = df[(df['Rating']>8) & (df['Votes']>100000)] And_df.head() # 多条件:OR - 所有评分大于8或Metascore超过90的电影 Or_df = df[(df['Rating']>8) | (df['Metascore']>80)] Or_df.head() # 多条件:NOT - 排除所有评分大于8或Metascore超过90的电影 Not_df = df[~((df['Rating']>8) | (df['Metascore']>80))] Not_df.head()

这一切都非常简单。然而,在某些时候可能需要复杂的过滤技术。

假设想要找到电影标题中单词数量大于或等于四个的行。如果负责这个操作,会怎么做?如果尝试以下操作,会得到一个错误。没有像使用series分割那样简单的方法。

# 单条件:所有评分大于8的电影数据框 df_gt_8 = df[df['Rating']>8] # 多条件:AND - 所有评分大于8且票数超过100000的电影 And_df = df[(df['Rating']>8) & (df['Votes']>100000)] # 多条件:OR - 所有评分大于8或Metascore超过90的电影 Or_df = df[(df['Rating']>8) | (df['Metascore']>80)] # 多条件:NOT - 排除所有评分大于8或Metascore超过90的电影 Not_df = df[~((df['Rating']>8) | (df['Metascore']>80))] new_df = df[len(df['Title'].split(" "))>=4]

一种方法是使用apply创建一个包含标题中单词数量的新列,然后根据该列进行过滤。

# 创建一个新列 df['num_words_title'] = df.apply(lambda x : len(x['Title'].split(" ")),axis=1) # 根据新列进行简单过滤 new_df = df[df['num_words_title']>=4] new_df.head()

只要不需要构建大量列,这是一个完全合适的情况。然而,更喜欢以下方法:

new_df = df[df.apply(lambda x : len(x['Title'].split(" "))>=4,axis=1)] new_df.head()

apply函数返回一个布尔值,可以用来过滤,这就是在这里所做的。现在知道了,只需要创建一个布尔列就可以进行过滤,可以在'apply()'语句中使用任何函数/逻辑来创建任何复杂的逻辑。

也使用apply来改变列类型,因为不想记住语法,而且它允许执行更多复杂的事情。在Pandas中,改变列类型的标准语法是astype。假设数据中有一个价格列,格式为str。这是可以做的事情:

df['Price'] = newDf['Price'].astype('int')

然而,这并不总是成功的。可能会收到以下消息:ValueError: '13,000'是一个不正确的long()字面量,基数为10。也就是说,带有“,”的字符串不能转换为int。要做到这一点,需要先去掉逗号。在多次遇到这个问题后,决定放弃astype,转而依赖apply来改变列类型。

df['Price'] = df.apply(lambda x: int(x['Price'].replace(',', '')),axis=1)

最后,还有progress_apply。(tqdm)包包含了一个名为progress_apply的函数。它帮助省下了很多时间。当数据中有很多行,或者最终编写了一个相当复杂的apply函数时,可能会注意到apply需要很长时间。在使用Spacy时,看到应用需要几个小时。在这种情况下,带有进度条的apply可能会很有用。可以使用(tqdm)来做到这一点。只需在笔记本顶部的初始导入后将apply替换为progress_apply,一切都会保持不变。

from tqdm import tqdm, tqdm_notebook tqdm_notebook().pandas() new_df['rating_custom'] = df.progress_apply(lambda x: custom_rating(x['Genre'],x['Rating']),axis=1) new_df.head()

结论:apply和lambda函数允许在修改数据时处理各种困难的任务。不认为需要担心使用Pandas时的任何事情,因为可以有效地使用它们。试图在本文中描述它的工作原理。可能有更多的方法可以实现刚刚描述的内容。然而,更喜欢apply/lambda而不是map/apply map,因为它更易于阅读,更适合工作流程。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485