codeye
2022/09/23阅读:39主题:默认主题
人们在不经意间使用的25个高级Pandas函数
人们在不经意间使用的25个高级Pandas函数 ExcelWriter, factorize, explode, squeeze, T, mask, idxmax, clip, ...
图片来自Pexels的Caleb Oquendo "我希望我可以在Pandas.... "中做这个操作。
嗯,很有可能,你可以
Pandas是如此庞大和深入,以至于它能让你执行你能想到的几乎所有表格操作。然而,这种浩瀚性有时也是一种劣势。
许多优雅的高级功能,解决了罕见的边缘情况和独特的场景,但却在文档中丢失了,被更常用的功能所掩盖。
本文旨在重新发现这些功能,并告诉你Pandas比你所知道的更棒。
-
ExcelWriter ExcelWriter是一个通用的类,用于创建excel文件(含工作表!),并向其中写入DataFrames。比方说,我们有这2个。
# Load two datasets
diamonds = sns.load_dataset("diamonds")
tips = sns.load_dataset("tips")
# Write to the same excel file
with pd.ExcelWriter("data/data.xlsx") as writer:
diamonds.to_excel(writer, sheet_name="diamonds")
tips.to_excel(writer, sheet_name="tips")
它有额外的属性来指定要使用的DateTime格式,你是想创建一个新的excel文件还是修改一个现有的文件,当一个工作表存在时,会发生什么,等等。请看文档中的细节。
2.pipe
pipe是Pandas中以简洁、紧凑的方式进行数据清理的最佳函数之一。它允许你将多个自定义函数链接成一个单一的操作。
例如,假设你有drop_duplicates、remove_outliers、encode_categoricals等函数,它们都接受各自的参数。下面是你如何在一个单一的操作中应用所有这三个函数。
df_preped = (diamonds.pipe(drop_duplicates).
pipe(remove_outliers, ['price', 'carat', 'depth']).
pipe(encode_categoricals, ['cut', 'color', 'clarity'])
)
我喜欢这个函数与Sklearn管道的相似之处。你可以用它做更多的事情,所以请查看文档或这篇有用的文章。
3.factorize factorize函数是Sklearn的LabelEncoder的一个pandas替代品。
# Mind the [0] at the end
diamonds["cut_enc"] = pd.factorize(diamonds["cut"])[0]
>>> diamonds["cut_enc"].sample(5)
52103 2
39813 0
31843 0
10675 0
6634 0
Name: cut_enc, dtype: int64
与LabelEncoder不同, factorize返回一个包含两个值的元组:编码的列和唯一类别的列表。
codes, unique = pd.factorize(diamonds["cut"], sort=True)
>>> codes[:10]
array([0, 1, 3, 1, 3, 2, 2, 2, 4, 2], dtype=int64)
>>> unique
['Ideal', 'Premium', 'Very Good', 'Good', 'Fair']
4.explode - 🤯🤯🤯🤯
一个有着有趣名字的函数是explode。让我们先看一个例子,然后再解释。
data = pd.Series([1, 6, 7, [46, 56, 49], 45, [15, 10, 12]]).to_frame("dirty")
>>> data
脏的那一列有两行,其中的值被记录为实际的列表。你可能经常在调查中看到这种类型的数据,因为有些问题接受多个答案。
>>> data.explode("dirty", ignore_index=True)
explode接收一个具有类似数组值的单元格并将其爆炸成多行。将ignore_index设置为True,以保持数字索引的顺序。
-
squeeze
图片来源:Cottonbro on Pexels 另一个名字古怪的函数是squeeze,用于非常罕见但令人讨厌的边缘情况。
subset = diamonds.loc[diamonds.index < 1, ["price"]]
>>> subset
其中一种情况是,当从一个用于对DataFrame进行子集的条件中返回一个单一的值。考虑一下这个例子。
>>> subset.squeeze()
326
尽管只有一个单元格,但它被作为一个DataFrame返回。这可能很烦人,因为你现在必须再次使用.loc的列名和索引来访问价格。
但是,如果你知道挤压,你就不必如此。该函数使你能够从一个单格的DataFrame或系列中删除一个轴。比如说。
subset.squeeze() 326 现在,只有标量被返回。也可以指定要删除的轴。
subset.squeeze("columns") # or "rows"
0 326
Name: price, dtype: int64
注意,squeeze只对单值的DataFrames或Series起作用。
-
between
一个相当灵巧的函数,用于在一个范围内对数字特征进行布尔索引。
# Get diamonds that are priced between 3500 and 3700 dollars
diamonds[diamonds["price"]\
.between(3500, 3700, inclusive="neither")].sample(5)
-
T
所有的DataFrames都有一个简单的T属性,它代表着转置。你可能不经常使用它,但我发现它在显示describe方法的DataFrames时相当有用。
>>> boston.description().T.head(10)
波士顿住房数据集有30个数字列。如果你按原样调用describe,DataFrame会在水平方向上伸展,很难比较统计数据。采取转置将切换轴线,以便在列中给出汇总统计数据。
-
Pandas Styler 你知道Pandas允许你对DataFrame进行造型吗?
它们有一个样式属性,这为定制和样式打开了大门,只受限于你的HTML和CSS知识。我不会讨论你能用样式做什么的全部细节,只向你展示我最喜欢的功能。
diabetes.describe().T.drop("count", axis=1)\
.style.highlight_max(color="darkred")
上面,我们正在突出显示持有某列最大值的单元格。另一个很酷的样式器是background_gradient,它可以根据列的值给它们一个渐变的背景颜色。
diabetes.describe().T.drop("count", axis=1).style.background_gradient(
subset=["mean", "50%"], cmap="Reds"
)
当你在一个有很多列的表格上使用describe,并想比较汇总统计时,这个功能就特别方便。点击这里查看该样式器的文档。
-
Pandas options 和Matplotlib一样,pandas也有全局设置,你可以通过调整来改变默认行为。
这些设置被分为5个模块。让我们看看display下有哪些设置。
dir(pd.options.display)
['chop_threshold',
'max_columns',
'max_colwidth',
'max_info_columns',
'max_info_rows',
'max_rows',
...
'precision',
'show_dimensions',
'unicode',
'width']
display下有很多选项,但我主要使用max_columns和recision。
你可以查看文档,深入挖掘这个奇妙的功能。
-
convert_dtypes 我们都知道,pandas有一个恼人的倾向,就是把一些列标记为对象数据类型。你可以使用convert_dtypes方法来代替手动指定它们的类型,该方法试图推断出最佳的数据类型。
sample = pd.read_csv(
"data/station_day.csv",
usecols=["StationId", "CO", "O3", "AQI_Bucket"],
)
>>> sample.dtypes
StationId object
CO float64
O3 float64
AQI_Bucket object
dtype: object
>>> sample.convert_dtypes().dtypes
StationId string
CO float64
O3 float64
AQI_Bucket string
dtype: object
不幸的是,由于不同日期时间格式的注意事项,它不能解析日期。
11.select_dtypes 我经常使用的一个函数是select_dtypes。我想从它的名字就可以看出这个函数的作用。它有包括和排除参数,你可以用来选择包括或排除某些数据类型的列。
例如,只用np.number来选择数字列。
# Choose only numerical columns
diamonds.select_dtypes(include=np.number).head()
exclude
或者排除它们。
# Exclude numerical columns
diamonds.select_dtypes(exclude=np.number).head()
12.mask
mask允许你快速替换自定义条件为真时的单元格值。
例如,假设我们有从50-60岁的人收集的调查数据。
# Create sample data
ages = pd.Series([55, 52, 50, 66, 57, 59, 49, 60]).to_frame("ages")
ages
我们将把50-60岁范围以外的年龄(有两个,49岁和66岁)视为数据输入错误,用NaN替换。
ages.mask(cond=~ages["ages"].between(50, 60), other=np.nan)
所以,掩码将不符合条件的数值替换为其他。
13.沿列轴的最小和最大 尽管min和max函数是众所周知的,但它们对于一些边缘案例还有另一个有用的属性。考虑一下这个数据集。
index = ["Diamonds", "Titanic", "Iris", "Heart Disease", "Loan Default"]
libraries = ["XGBoost", "CatBoost", "LightGBM", "Sklearn GB"]
df = pd.DataFrame(
{lib: np.random.uniform(90, 100, 5) for lib in libraries}, index=index
)
>>> df
上面的假DataFrame是4个不同的梯度提升库在5个数据集上的点性能。我们想找到在每个数据集上表现最好的库。这里是你如何用max优雅地完成它。
>>> df.max(axis=1)
Diamonds 99.52684
Titanic 99.63650
Iris 99.10989
Heart Disease 99.31627
Loan Default 97.96728
dtype: float64
只要把轴改成1,就可以得到一个行间的最大/最小值。
-
nlargest和nsmallest 有时你不只是想知道一列的最小/最大值。你想看到一个变量的前N个或~(前N个)值。这就是nlargest和nsmallest派上用场的地方。
让我们看看前5个最贵和最便宜的钻石。
diamonds.nlargest(5, "price")
diamonds.nsmallest(5, "price")
15.idxmax和idxmin
当你在一列上调用max或min时,pandas返回最大/最小的值。然而,有时你想知道最小/最大的位置,这在这些函数中是不可能的。
>>> diamonds.price.idxmax()
27749
>>> diamonds.carat.idxmin()
14
相反,你应该使用idxmax/idxmin。
你也可以指定列轴,在这种情况下,这些函数会返回该列的索引号。
-
value_counts with dropna=False
查找缺失值百分比的一个常见操作是将isnull和sum连在一起,然后除以数组的长度。
但是,你可以用value_counts的相关参数做同样的事情。
ames_housing = pd.read_csv("data/train.csv")
>>> ames_housing["FireplaceQu"].value_counts(dropna=False, normalize=True)
NaN 0.47260
Gd 0.26027
TA 0.21438
Fa 0.02260
Ex 0.01644
Po 0.01370
Name: FireplaceQu, dtype: float64
Fireplace质量的Ames住房数据集由47%的nulls组成。
17.剪辑
照片:Ann H on Pexels 在数据分析中,离群点的检测和去除是很常见的。
clip函数可以非常容易地找到范围外的离群值,并将其替换为硬限值。
让我们回到年龄的例子。
这一次,我们将用50和60的硬限值来替换超出范围的年龄。
ages.clip(50, 60)
又快又有效!
-
at_time和between_time
在处理具有高粒度的时间序列时,这两个可能很有用。
at_time允许你对某一特定日期或时间的值进行子集。考虑一下这个时间序列。
让我们选择下午3点的所有行。
index = pd.date_range("2021-08-01", periods=100, freq="H")
data = pd.DataFrame({"col": list(range(100))}, index=index)
>>> data.head()
>>> data.at_time("15:00")
很酷,是吧?现在,让我们使用between_time来选择一个自定义间隔内的记录。
from datetime import datetime
data.between_time("09:45", "12:00")
请注意,这两个函数都需要一个DateTimeIndex,而且它们只对时间(如o'clock)有效。如果你想在一个DateTime区间内进行子集,请使用between。
19.bdate_range bdate_range是一个简短的函数,用于创建具有营业日频率的TimeSeries指数。
series = pd.bdate_range("2021-01-01", "2021-01-31") # A period of one month
>>> len(series)
21
营业日的频率在金融界很常见。因此,这个函数在用reindex函数对现有的时间序列进行重新索引时可能会派上用场。
20.autocorr 时间序列分析中的一个关键部分是检查一个变量的自相关。
自相关就是普通的相关系数,但它是用时间序列的滞后版本计算的。
更详细地说,滞后=k的时间序列的自相关的计算方法如下。
time_series = tips[["tip"]]
time_series["lag_1"] = time_series["tip"].shift(1)
time_series["lag_2"] = time_series["tip"].shift(2)
time_series["lag_3"] = time_series["tip"].shift(3)
time_series["lag_4"] = time_series["tip"].shift(4)
# time_series['lag_k'] = time_series['tip'].shift(k)
>>> time_series.head()
# Autocorrelation of tip at lag_10
>>> time_series["tip"].autocorr(lag=8)
0.07475238789967077
时间序列被移到k期。
-
计算原始提示和每个滞后_*之间的相关性。
你可以使用Pandas的autocorr函数,而不是手动做这一切。
你可以从这个帖子中了解更多关于自相关在时间序列分析中的重要性。
-
hasnans
Pandas提供了一个快速的方法来检查一个给定的序列是否包含有hasans属性的空值。
series = pd.Series([2, 4, 6, "sadf", np.nan])
>>> series.hasnans
True
根据其文档,它可以实现各种性能的提升。请注意,该属性只对pd.Series有效。
22.at和iat
# [index, label]
>>> diamonds.at[234, "cut"]
'Ideal'
# [index, index]
>>> diamonds.iat[1564, 4]
61.2
# Replace 16541th row of the price column
>>> diamonds.at[16541, "price"] = 10000
这两个访问器比loc和iloc快得多,但有一个缺点。它们一次只允许选择或替换一个值。
-
argsort
tips.reset_index(inplace=True, drop=True)
sort_idx = tips["total_bill"].argsort(kind="mergesort")
# Now, sort `tips` based on total_bill
tips.iloc[sort_idx].head()
当你想提取对一个数组进行排序的索引时,你应该使用这个函数。png 图片由作者提供
24.cat accessor 众所周知,Pandas能够使用访问器(如dt或str)对日期和字符串使用内置的Python函数。
>>> diamonds.dtypes
carat float64
cut category
color category
clarity category
depth float64
table float64
price int64
x float64
y float64
z float64
cut_enc int64
dtype: object
Pandas也有一个特殊的类别数据类型,用于分类变量,如下图所示。
当一个列是类别时,你可以使用cat访问器使用几个特殊的函数。例如,让我们看看钻石切割的独特类别。
>>> diamonds["cut"].cat.categories
['Ideal', 'Premium', 'Very Good', 'Good', 'Fair']
还有一些函数,如remove_categories或rename_categories,等等。
diamonds["new_cuts"] = diamonds["cut"].cat.rename_categories(list("ABCDE"))
>>> diamonds["new_cuts"].cat.categories
Index(['A', 'B', 'C', 'D', 'E'], dtype='object')
你可以在这里看到cat访问器下的全部功能列表。
-
GroupBy.nth
这个函数只对GroupBy对象起作用。具体来说,在分组后,第n次返回每组的第n行。
>>> diamonds.groupby("cut").nth(5)
总结
尽管Dask和datatable这样的库正以其处理海量数据集的闪亮的新功能慢慢赢Pandas, 但Pandas仍然是Python数据科学生态系统中最广泛使用的数据操作工具。这个库是其他软件包模仿和改进的榜样,因为它能很好地集成到现代SciPy堆栈中。
谢谢您的阅读!
作者介绍