【51CTO.com快译】 你用Python准备数据时,Pandas库提供了核心功能。但许多人只了解基础的方法,本文介绍的这些鲜为人知的高级方法让你更轻松整洁地处理数据。
Pandas是数据界的典型库。由于能够加载、过滤、处理和浏览数据,难怪它备受数据科学家的喜爱。
大多数人自然会坚守Pandas很基础的方法。从CSV文件加载数据,过滤几列,然后直接进入到数据可视化。不过Pandas实际上有许多鲜为人知但实用的功能,可以使数据处理起来轻松得多,整洁得多。
本教程将介绍5项更高级的功能、它们的功用及使用方法。
(1)配置选项和设置
Pandas带有一组用户可配置的选项和设置。它们能大大提高生产力,因为你可以根据自己的喜好来定制Pandas环境。
比如说,我们可以更改Pandas的一些显示设置,改变显示的行数和列数以及显示的精度浮点数。
- import pandas as pd
- display_settings = {
- 'max_columns': 10,
- 'expand_frame_repr': True, # Wrap to multiple pages
- 'max_rows': 10,
- 'precision': 2,
- 'show_dimensions': True
- }
- for op, value in display_settings.items():
- pd.set_option("display.{}".format(op), value)
上面的代码确保Pandas始终最多显示10行和10列,浮点值最多显示2个小数位。这样,我们尝试打印大的DataFrame时,终端或Jupyter Notebook不会看起来一团糟!
这只是个基本的例子。除了简单的显示设置外,还有很多设置可以探索。可以查看官方文档中的所有选项(https://pandas.pydata.org/pandas-docs/stable/user_guide/options.html)。
(2)合并DataFrame
Pandas DataFrame一个相对不为人知的地方是,实际上有两种不同的方法来合并。每种方法得到的结果不一样,因此,根据你要实现的目标选择合适的方法很重要。此外,它们含有许多进一步定制合并的参数。不妨看一下。
连接
连接是合并DataFrame的最著名方法,它好比“堆叠”。这种堆叠可以横向或纵向进行。
假设你有一个庞大的CSV格式的数据集。将它分成多个文件以便处理合情合理(这是大型数据集的常见做法,名为分片)。
将其加载到Pandas后,你可以纵向堆叠每个CSV的DataFrame,为所有数据创建一个大的DataFrame。假设我们有3个分片,每个分片有500万行,那么在纵向堆叠所有分片后,最终的DataFrame会有1500万行。
下面的代码显示了如何在Pandas中纵向连接DataFrame。
- # Vertical concat
- pd.concat([october_df, november_df, december_df], axis=0)
你可以按照列而不是按照行来拆分数据集,执行类似的操作——每个CSV文件有几列(包含数据集的所有行)。就像我们将数据集的特征划分为不同的分片那样。然后,你可以横向堆叠它们以合并那些列/特征。
- # Horizontal concat
- pd.concat([features_1to5_df, features_6to10_df, features_11to15_df], axis=1)
合并
合并更复杂但功能更强大,以类似SQL的方式合并Pandas DataFrame,即DataFrame将通过某个常见属性加以连接。
假设你有描述YouTube频道的两个DataFrame。其中一个含有用户ID列表和每个用户在频道上总共花费的时间。另一个含有类似的用户ID列表和每个用户看过多少视频。合并使我们可以通过匹配用户ID,然后将ID、花费的时间和视频数量归入到每个用户的单行,即可将两个DataFrame合并为一个。
合并Pandas中的两个DataFrame通过合并函数来完成。你可以在下面的代码中看到其工作方式。left和right参数是指你希望合并的两个DataFrame,而on指定了用于匹配的列。
- pd.merge(left=ids_and_time_df,
- right=ids_and_videos_df,
- on="id")
为了进一步模拟SQL连接,how参数让你可以选择想要执行的类似SQL的连接的类型:内、外、左或右。想了解SQL连接的更多信息,请参阅W3Schools教程(https://www.w3schools.com/sql/sql_join.asp)。
(3)重塑DataFrame
有几种方法可以重塑和重组Pandas DataFrame。既有简单易用的方法,也有强大复杂的方法。不妨看看最常见的三种方法。针对以下所有示例,我们将使用超级英雄的这个数据集!
- import pandas as pd
- players_data = {'Player': ['Superman', 'Batman', 'Thanos', 'Batman', 'Thanos',
- 'Superman', 'Batman', 'Thanos', 'Black Widow', 'Batman', 'Thanos', 'Superman'],
- 'Year': [2000,2000,2000,2001,2001,2002,2002,2002,2003,2004,2004,2005],
- 'Points':[23,43,45,65,76,34,23,78,89,76,92,87]}
- df = pd.DataFrame(players_data)
- print(df)
- """
- Player Year Points
- 0 Superman 2000 23
- 1 Batman 2000 43
- 2 Thanos 2000 45
- 3 Batman 2001 65
- 4 Thanos 2001 76
- 5 Superman 2002 34
- 6 Batman 2002 23
- 7 Thanos 2002 78
- 8 Black Widow 2003 89
- 9 Batman 2004 76
- 10 Thanos 2004 92
- 11 Superman 2005 87
- """
转置
转置是其中最简单的。转置将DataFrame的行与列进行互换。如果你有5000行和10列,然后转置你的DataFrame后,最终会得到10行和5000列。
- import pandas as pd
- players_data = {'Player': ['Superman', 'Batman', 'Thanos', 'Batman', 'Thanos',
- 'Superman', 'Batman', 'Thanos', 'Black Widow', 'Batman', 'Thanos', 'Superman'],
- 'Year': [2000,2000,2000,2001,2001,2002,2002,2002,2003,2004,2004,2005],
- 'Points':[23,43,45,65,76,34,23,78,89,76,92,87]}
- df = pd.DataFrame(players_data)
- print(df)
- """
- Player Year Points
- 0 Superman 2000 23
- 1 Batman 2000 43
- 2 Thanos 2000 45
- 3 Batman 2001 65
- 4 Thanos 2001 76
- 5 Superman 2002 34
- 6 Batman 2002 23
- 7 Thanos 2002 78
- 8 Black Widow 2003 89
- 9 Batman 2004 76
- 10 Thanos 2004 92
- 11 Superman 2005 87
- """
Groupby
Groupby的主要用途是根据一些键将DataFrame分成多个部分。一旦DataFrame拆分成多个部分,你可以执行遍历、对每个部分独立执行一些操作。
比如说,我们可以从下面的代码中看到如何创建了有相应年份和积分的玩家DataFrame。然后我们执行groupby,根据玩家将DataFrame分为多个部分。因此,每个玩家都有自己的组,显示该玩家每年玩游戏时获得了多少积分。
- groups_df = df.groupby('Player')
- for player, group in groups_df:
- print("----- {} -----".format(player))
- print(group)
- print("")
- ### This prints out the following
- """
- ----- Batman -----
- Player Year Points
- 1 Batman 2000 43
- 3 Batman 2001 65
- 6 Batman 2002 23
- 9 Batman 2004 76
- ----- Black Widow -----
- Player Year Points
- 8 Black Widow 2003 89
- ----- Superman -----
- Player Year Points
- 0 Superman 2000 23
- 5 Superman 2002 34
- 11 Superman 2005 87
- ----- Thanos -----
- Player Year Points
- 2 Thanos 2000 45
- 4 Thanos 2001 76
- 7 Thanos 2002 78
- 10 Thanos 2004 92
- """
堆叠
堆叠将DataFrame转换成有多级索引,即每行有多个子部分。这些子部分是使用DataFrame的列创建的,并将其压缩成多索引。总体而言,可以将堆叠视为将列压缩成多索引行。
可以通过示例来说明,如下所示。
- df = df.stack()
- print(df)
- """
- 0 Player Superman
- Year 2000
- Points 23
- 1 Player Batman
- Year 2000
- Points 43
- 2 Player Thanos
- Year 2000
- Points 45
- 3 Player Batman
- Year 2001
- Points 65
- 4 Player Thanos
- Year 2001
- Points 76
- 5 Player Superman
- Year 2002
- Points 34
- 6 Player Batman
- Year 2002
- Points 23
- 7 Player Thanos
- Year 2002
- Points 78
- 8 Player Black Widow
- Year 2003
- Points 89
- 9 Player Batman
- Year 2004
- Points 76
- 10 Player Thanos
- Year 2004
- Points 92
- 11 Player Superman
- Year 2005
- Points 87
- """
(4)处理时间数据
Datetime库是Python的基本库。只要你处理与实际日期和时间信息有关的任何东西,它都是值得你使用的库。幸好,Pandas还有使用Datetime对象的功能。
不妨举例说明。在下面的代码中,我们先创建一个有4列的DataFrame:Day、Month、Year和data,然后按年和月进行排序。如你所见,这非常混乱。仅仅为了存储日期,我们就用了3列,实际上我们知道日历日期只是一个值。
- from itertools import product
- import pandas as pd
- import numpy as np
- col_names = ["Day", "Month", "Year"]
- df = pd.DataFrame(list(product([10, 11, 12], [8, 9], [2018, 2019])),
- columns=col_names)
- df['data'] = np.random.randn(len(df))
- df = df.sort_values(['Year', 'Month'], ascending=[True, True])
- print(df)
- """
- Day Month Year data
- 0 10 8 2018 1.685356
- 4 11 8 2018 0.441383
- 8 12 8 2018 1.276089
- 2 10 9 2018 -0.260338
- 6 11 9 2018 0.404769
- 10 12 9 2018 -0.359598
- 1 10 8 2019 0.145498
- 5 11 8 2019 -0.731463
- 9 12 8 2019 -1.451633
- 3 10 9 2019 -0.988294
- 7 11 9 2019 -0.687049
- 11 12 9 2019 -0.067432
- """
我们可以用datetime来清理。
Pandas贴心地随带名为to_datetime()的函数,它可以压缩多个DataFrame列并将其转换成单个Datetime对象。一旦采用这种格式,你可以享用Datetime库的所有灵活性。
想使用to_datetime()函数,需要将相关列中的所有“data”数据传递给它。那就是“Day”、“Month”和“Year”这三列。一旦有了Datetime格式的内容,我们不再需要其他列,删除即可。看看下面的代码,看看它们如何工作!
- from itertools import product
- import pandas as pd
- import numpy as np
- col_names = ["Day", "Month", "Year"]
- df = pd.DataFrame(list(product([10, 11, 12], [8, 9], [2018, 2019])),
- columns=col_names)
- df['data'] = np.random.randn(len(df))
- df = df.sort_values(['Year', 'Month'], ascending=[True, True])
- df.insert(loc=0, column="date", value=pd.to_datetime(df[col_names]))
- df = df.drop(col_names, axis=1).squeeze()
- print(df)
- """
- date data
- 0 2018-08-10 -0.328973
- 4 2018-08-11 -0.670790
- 8 2018-08-12 -1.360565
- 2 2018-09-10 -0.401973
- 6 2018-09-11 -1.238754
- 10 2018-09-12 0.957695
- 1 2019-08-10 0.571126
- 5 2019-08-11 -1.320735
- 9 2019-08-12 0.196036
- 3 2019-09-10 -1.717800
- 7 2019-09-11 0.074606
- 11 2019-09-12 -0.643198
- """
(5)将项映射到组
映射是个巧妙的技巧,有助于对分类数据进行组织。比如设想我们有一个庞大的DataFrame,有成千上万行,其中一列含有我们想要分类的项。这么做可以大大简化机器学习模型的训练和有效地可视化数据。
请查看下面的代码,这个小示例表明了我们想要分类的食品列表。
- import pandas as pd
- foods = pd.Series(["Bread", "Rice", "Steak", "Ham", "Chicken",
- "Apples", "Potatoes", "Mangoes", "Fish",
- "Bread", "Rice", "Steak", "Ham", "Chicken",
- "Apples", "Potatoes", "Mangoes", "Fish",
- "Apples", "Potatoes", "Mangoes", "Fish",
- "Apples", "Potatoes", "Mangoes", "Fish",
- "Bread", "Rice", "Steak", "Ham", "Chicken",
- "Bread", "Rice", "Steak", "Ham", "Chicken",
- "Bread", "Rice", "Steak", "Ham", "Chicken",
- "Apples", "Potatoes", "Mangoes", "Fish",
- "Apples", "Potatoes", "Mangoes", "Fish",
- "Apples", "Potatoes", "Mangoes", "Fish",
- "Bread", "Rice", "Steak", "Ham", "Chicken",
- "Bread", "Rice", "Steak", "Ham", "Chicken",])
- groups_dict = {
- "Protein": ["Steak", "Ham", "Chicken", "Fish"],
- "Carbs": ["Bread", "Rice", "Apples", "Potatoes", "Mangoes"]
- }
在上面的代码中,我们将列表放入到Pandas系列。我们还创建了一个字典,显示了想要的映射,将每个食品项分类成“Protein”或“Carbs”。这是尝试性质的示例,但如果该系列规模很大,假设有1000000项 ,那么遍历它根本不可行。
我们可以使用Pandas内置的.map()函数编写函数,以优化的方式执行映射,而不是使用基本的for-loop。请查看下面的代码,看看该函数及使用方式。
- def membership_map(pandas_series, groups_dict):
- groups = {x: k for k, v in groups_dict.items() for x in v}
- mapped_series = pandas_series.map(groups)
- return mapped_series
- mapped_data = membership_map(foods, groups_dict)
- print(list(mapped_data))
在该函数中,我们先遍历字典以创建一个新的字典,其中的键代表Pandas系列中每个可能的项,值代表新的映射项:“Protein”或“Carbs”。 然后,我们只需使用Pandas的内置map函数来映射该系列中的所有值。
不妨看看下面的输出以查看结果!
['Carbs', 'Carbs', 'Protein', 'Protein', 'Protein', 'Carbs', 'Carbs', 'Carbs', 'Protein', 'Carbs', 'Carbs', 'Protein', 'Protein', 'Protein', 'Carbs', 'Carbs', 'Carbs', 'Protein', 'Carbs', 'Carbs', 'Carbs', 'Protein', 'Carbs', 'Carbs', 'Carbs', 'Protein', 'Carbs', 'Carbs', 'Protein', 'Protein', 'Protein', 'Carbs', 'Carbs', 'Protein', 'Protein', 'Protein', 'Carbs', 'Carbs', 'Protein', 'Protein', 'Protein', 'Carbs', 'Carbs', 'Carbs', 'Protein', 'Carbs', 'Carbs', 'Carbs', 'Protein', 'Carbs', 'Carbs', 'Carbs', 'Protein', 'Carbs', 'Carbs', 'Protein', 'Protein', 'Protein', 'Carbs', 'Carbs', 'Protein', 'Protein', 'Protein']
原文标题:5 Advanced Features of Pandas and How to Use Them,作者:George Seif
【51CTO译稿,合作站点转载请注明原文译者和出处为51CTO.com】