Python 数据分析(PYDA)第三版(二)(4)

简介: Python 数据分析(PYDA)第三版(二)

Python 数据分析(PYDA)第三版(二)(3)https://developer.aliyun.com/article/1482376


函数应用和映射

NumPy ufuncs(逐元素数组方法)也适用于 pandas 对象:

In [223]: frame = pd.DataFrame(np.random.standard_normal((4, 3)),
 .....:                      columns=list("bde"),
 .....:                      index=["Utah", "Ohio", "Texas", "Oregon"])
In [224]: frame
Out[224]: 
 b         d         e
Utah   -0.204708  0.478943 -0.519439
Ohio   -0.555730  1.965781  1.393406
Texas   0.092908  0.281746  0.769023
Oregon  1.246435  1.007189 -1.296221
In [225]: np.abs(frame)
Out[225]: 
 b         d         e
Utah    0.204708  0.478943  0.519439
Ohio    0.555730  1.965781  1.393406
Texas   0.092908  0.281746  0.769023
Oregon  1.246435  1.007189  1.296221

另一个频繁的操作是将一个一维数组上的函数应用于每列或每行。DataFrame 的apply方法正是这样做的:

In [226]: def f1(x):
 .....:     return x.max() - x.min()
In [227]: frame.apply(f1)
Out[227]: 
b    1.802165
d    1.684034
e    2.689627
dtype: float64

这里的函数f计算 Series 的最大值和最小值之间的差异,对frame中的每列调用一次。结果是一个具有frame列作为其索引的 Series。

如果将axis="columns"传递给apply,则该函数将每行调用一次。将其视为"跨列应用"是一种有用的方式:

In [228]: frame.apply(f1, axis="columns")
Out[228]: 
Utah      0.998382
Ohio      2.521511
Texas     0.676115
Oregon    2.542656
dtype: float64

许多最常见的数组统计(如summean)都是 DataFrame 方法,因此不需要使用apply

传递给apply的函数不必返回标量值;它也可以返回具有多个值的 Series:

In [229]: def f2(x):
 .....:     return pd.Series([x.min(), x.max()], index=["min", "max"])
In [230]: frame.apply(f2)
Out[230]: 
 b         d         e
min -0.555730  0.281746 -1.296221
max  1.246435  1.965781  1.393406

也可以使用逐元素 Python 函数。假设您想要从frame中的每个浮点值计算格式化字符串。您可以使用applymap来实现:

In [231]: def my_format(x):
 .....:     return f"{x:.2f}"
In [232]: frame.applymap(my_format)
Out[232]: 
 b     d      e
Utah    -0.20  0.48  -0.52
Ohio    -0.56  1.97   1.39
Texas    0.09  0.28   0.77
Oregon   1.25  1.01  -1.30

applymap的命名原因是 Series 有一个map方法,用于应用逐元素函数:

In [233]: frame["e"].map(my_format)
Out[233]: 
Utah      -0.52
Ohio       1.39
Texas      0.77
Oregon    -1.30
Name: e, dtype: object

排序和排名

按某个标准对数据集进行排序是另一个重要的内置操作。要按行或列标签的字典顺序排序,请使用sort_index方法,该方法返回一个新的排序对象:

In [234]: obj = pd.Series(np.arange(4), index=["d", "a", "b", "c"])
In [235]: obj
Out[235]: 
d    0
a    1
b    2
c    3
dtype: int64
In [236]: obj.sort_index()
Out[236]: 
a    1
b    2
c    3
d    0
dtype: int64

对于 DataFrame,您可以在任一轴上按索引排序:

In [237]: frame = pd.DataFrame(np.arange(8).reshape((2, 4)),
 .....:                      index=["three", "one"],
 .....:                      columns=["d", "a", "b", "c"])
In [238]: frame
Out[238]: 
 d  a  b  c
three  0  1  2  3
one    4  5  6  7
In [239]: frame.sort_index()
Out[239]: 
 d  a  b  c
one    4  5  6  7
three  0  1  2  3
In [240]: frame.sort_index(axis="columns")
Out[240]: 
 a  b  c  d
three  1  2  3  0
one    5  6  7  4

默认情况下,数据按升序排序,但也可以按降序排序:

In [241]: frame.sort_index(axis="columns", ascending=False)
Out[241]: 
 d  c  b  a
three  0  3  2  1
one    4  7  6  5

要按值对 Series 进行排序,请使用其sort_values方法:

In [242]: obj = pd.Series([4, 7, -3, 2])
In [243]: obj.sort_values()
Out[243]: 
2   -3
3    2
0    4
1    7
dtype: int64

默认情况下,任何缺失值都按顺序排在 Series 的末尾:

In [244]: obj = pd.Series([4, np.nan, 7, np.nan, -3, 2])
In [245]: obj.sort_values()
Out[245]: 
4   -3.0
5    2.0
0    4.0
2    7.0
1    NaN
3    NaN
dtype: float64

缺失值也可以通过使用na_position选项将其排序到开头:

In [246]: obj.sort_values(na_position="first")
Out[246]: 
1    NaN
3    NaN
4   -3.0
5    2.0
0    4.0
2    7.0
dtype: float64

在对 DataFrame 进行排序时,可以使用一个或多个列中的数据作为排序键。为此,请将一个或多个列名传递给sort_values

In [247]: frame = pd.DataFrame({"b": [4, 7, -3, 2], "a": [0, 1, 0, 1]})
In [248]: frame
Out[248]: 
 b  a
0  4  0
1  7  1
2 -3  0
3  2  1
In [249]: frame.sort_values("b")
Out[249]: 
 b  a
2 -3  0
3  2  1
0  4  0
1  7  1

要按多个列排序,请传递一个名称列表:

In [250]: frame.sort_values(["a", "b"])
Out[250]: 
 b  a
2 -3  0
0  4  0
3  2  1
1  7  1

排名从数组中的最低值开始,为数组中的每个有效数据点分配从 1 到数据点数量的等级。Series 和 DataFrame 的rank方法是要查看的地方;默认情况下,rank通过为每个组分配平均等级来打破平局:

In [251]: obj = pd.Series([7, -5, 7, 4, 2, 0, 4])
In [252]: obj.rank()
Out[252]: 
0    6.5
1    1.0
2    6.5
3    4.5
4    3.0
5    2.0
6    4.5
dtype: float64

排名也可以根据它们在数据中观察到的顺序进行分配:

In [253]: obj.rank(method="first")
Out[253]: 
0    6.0
1    1.0
2    7.0
3    4.0
4    3.0
5    2.0
6    5.0
dtype: float64

在这里,与使用条目 0 和 2 的平均等级 6.5 不同,它们分别设置为 6 和 7,因为标签 0 在数据中位于标签 2 之前。

您也可以按降序排名:

In [254]: obj.rank(ascending=False)
Out[254]: 
0    1.5
1    7.0
2    1.5
3    3.5
4    5.0
5    6.0
6    3.5
dtype: float64

请参阅表 5.6 以获取可用的平局破解方法列表。

DataFrame 可以在行或列上计算排名:

In [255]: frame = pd.DataFrame({"b": [4.3, 7, -3, 2], "a": [0, 1, 0, 1],
 .....:                       "c": [-2, 5, 8, -2.5]})
In [256]: frame
Out[256]: 
 b  a    c
0  4.3  0 -2.0
1  7.0  1  5.0
2 -3.0  0  8.0
3  2.0  1 -2.5
In [257]: frame.rank(axis="columns")
Out[257]: 
 b    a    c
0  3.0  2.0  1.0
1  3.0  1.0  2.0
2  1.0  2.0  3.0
3  3.0  2.0  1.0

表 5.6:排名的平局破解方法

方法 描述
"average" 默认:为相等组中的每个条目分配平均等级
"min" 使用整个组的最小等级
"max" 使用整个组的最大等级
"first" 按数据中值出现的顺序分配等级
"dense" 类似于method="min",但等级总是在组之间增加 1,而不是在组中相等元素的数量之间增加

具有重复标签的轴索引

到目前为止,我们看过的几乎所有示例都具有唯一的轴标签(索引值)。虽然许多 pandas 函数(如reindex)要求标签是唯一的,但这并非强制要求。让我们考虑一个具有重复索引的小 Series:

In [258]: obj = pd.Series(np.arange(5), index=["a", "a", "b", "b", "c"])
In [259]: obj
Out[259]: 
a    0
a    1
b    2
b    3
c    4
dtype: int64

索引的is_unique属性可以告诉您其标签是否唯一:

In [260]: obj.index.is_unique
Out[260]: False

数据选择是与重复不同的主要行为之一。索引具有多个条目的标签返回一个 Series,而单个条目返回一个标量值:

In [261]: obj["a"]
Out[261]: 
a    0
a    1
dtype: int64
In [262]: obj["c"]
Out[262]: 4

这可能会使您的代码变得更加复杂,因为根据标签是否重复,索引的输出类型可能会有所不同。

相同的逻辑也适用于 DataFrame 中的行(或列)索引:

In [263]: df = pd.DataFrame(np.random.standard_normal((5, 3)),
 .....:                   index=["a", "a", "b", "b", "c"])
In [264]: df
Out[264]: 
 0         1         2
a  0.274992  0.228913  1.352917
a  0.886429 -2.001637 -0.371843
b  1.669025 -0.438570 -0.539741
b  0.476985  3.248944 -1.021228
c -0.577087  0.124121  0.302614
In [265]: df.loc["b"]
Out[265]: 
 0         1         2
b  1.669025 -0.438570 -0.539741
b  0.476985  3.248944 -1.021228
In [266]: df.loc["c"]
Out[266]: 
0   -0.577087
1    0.124121
2    0.302614
Name: c, dtype: float64

5.3 总结和计算描述性统计

pandas 对象配备了一组常见的数学和统计方法。其中大多数属于减少摘要统计的类别,这些方法从 Series 中提取单个值(如总和或均值),或者从 DataFrame 的行或列中提取一系列值。与 NumPy 数组上找到的类似方法相比,它们内置了对缺失数据的处理。考虑一个小的 DataFrame:

In [267]: df = pd.DataFrame([[1.4, np.nan], [7.1, -4.5],
 .....:                    [np.nan, np.nan], [0.75, -1.3]],
 .....:                   index=["a", "b", "c", "d"],
 .....:                   columns=["one", "two"])
In [268]: df
Out[268]: 
 one  two
a  1.40  NaN
b  7.10 -4.5
c   NaN  NaN
d  0.75 -1.3

调用 DataFrame 的sum方法会返回一个包含列和的 Series:

In [269]: df.sum()
Out[269]: 
one    9.25
two   -5.80
dtype: float64

传递axis="columns"axis=1会跨列求和:

In [270]: df.sum(axis="columns")
Out[270]: 
a    1.40
b    2.60
c    0.00
d   -0.55
dtype: float64

当整行或整列包含所有 NA 值时,总和为 0,而如果任何值不是 NA,则结果为 NA。可以使用skipna选项禁用此功能,在这种情况下,行或列中的任何 NA 值都会使相应的结果为 NA:

In [271]: df.sum(axis="index", skipna=False)
Out[271]: 
one   NaN
two   NaN
dtype: float64
In [272]: df.sum(axis="columns", skipna=False)
Out[272]: 
a     NaN
b    2.60
c     NaN
d   -0.55
dtype: float64

一些聚合,如mean,需要至少一个非 NA 值才能产生一个值结果,因此我们有:

In [273]: df.mean(axis="columns")
Out[273]: 
a    1.400
b    1.300
c      NaN
d   -0.275
dtype: float64

请参见表 5.7 以获取每种减少方法的常见选项列表。

表 5.7:减少方法的选项

方法 描述
axis 要减少的轴;DataFrame 的行为“index”,列为“columns”
skipna 排除缺失值;默认为True
level 如果轴是分层索引(MultiIndex),则按级别减少

一些方法,如idxminidxmax,返回间接统计信息,如达到最小值或最大值的索引值:

In [274]: df.idxmax()
Out[274]: 
one    b
two    d
dtype: object

其他方法是累积

In [275]: df.cumsum()
Out[275]: 
 one  two
a  1.40  NaN
b  8.50 -4.5
c   NaN  NaN
d  9.25 -5.8

一些方法既不是减少也不是累积。describe就是一个例子,一次生成多个摘要统计信息:

In [276]: df.describe()
Out[276]: 
 one       two
count  3.000000  2.000000
mean   3.083333 -2.900000
std    3.493685  2.262742
min    0.750000 -4.500000
25%    1.075000 -3.700000
50%    1.400000 -2.900000
75%    4.250000 -2.100000
max    7.100000 -1.300000

对于非数字数据,describe会生成替代的摘要统计信息:

In [277]: obj = pd.Series(["a", "a", "b", "c"] * 4)
In [278]: obj.describe()
Out[278]: 
count     16
unique     3
top        a
freq       8
dtype: object

请参见表 5.8 以获取摘要统计和相关方法的完整列表。

表 5.8:描述性和摘要统计

方法 描述
count 非 NA 值的数量
describe 计算一组摘要统计信息
min, max 计算最小值和最大值
argmin, argmax 计算获得最小值或最大值的索引位置(整数),分别;在 DataFrame 对象上不可用
idxmin, idxmax 计算获得最小值或最大值的索引标签
quantile 计算从 0 到 1 范围的样本分位数(默认值:0.5)
sum 值的总和
mean 值的均值
median 值的算术中位数(50%分位数)
mad 与均值的平均绝对偏差
prod 所有值的乘积
var 值的样本方差
std 值的样本标准差
skew 值的样本偏度(第三时刻)
kurt 值的样本峰度(第四时刻)
cumsum 值的累积和
cummin, cummax 值的累积最小值或最大值,分别
cumprod 值的累积乘积
diff 计算第一个算术差异(对时间序列有用)
pct_change 计算百分比变化

相关性和协方差

一些摘要统计信息,如相关性和协方差,是从一对参数计算得出的。让我们考虑一些股票价格和成交量的 DataFrame,最初从 Yahoo! Finance 获取,并在本书的附带数据集中以二进制 Python pickle 文件的形式提供:

In [279]: price = pd.read_pickle("examples/yahoo_price.pkl")
In [280]: volume = pd.read_pickle("examples/yahoo_volume.pkl")

现在我计算价格的百分比变化,这是一个时间序列操作,将在第十一章:时间序列中进一步探讨:

In [281]: returns = price.pct_change()
In [282]: returns.tail()
Out[282]: 
 AAPL      GOOG       IBM      MSFT
Date 
2016-10-17 -0.000680  0.001837  0.002072 -0.003483
2016-10-18 -0.000681  0.019616 -0.026168  0.007690
2016-10-19 -0.002979  0.007846  0.003583 -0.002255
2016-10-20 -0.000512 -0.005652  0.001719 -0.004867
2016-10-21 -0.003930  0.003011 -0.012474  0.042096

Series 的corr方法计算两个 Series 中重叠的、非 NA、按索引对齐的值的相关性。相关地,cov计算协方差:

In [283]: returns["MSFT"].corr(returns["IBM"])
Out[283]: 0.49976361144151166
In [284]: returns["MSFT"].cov(returns["IBM"])
Out[284]: 8.870655479703549e-05

另一方面,DataFrame 的corrcov方法分别返回完整的相关性或协方差矩阵作为 DataFrame:

In [285]: returns.corr()
Out[285]: 
 AAPL      GOOG       IBM      MSFT
AAPL  1.000000  0.407919  0.386817  0.389695
GOOG  0.407919  1.000000  0.405099  0.465919
IBM   0.386817  0.405099  1.000000  0.499764
MSFT  0.389695  0.465919  0.499764  1.000000
In [286]: returns.cov()
Out[286]: 
 AAPL      GOOG       IBM      MSFT
AAPL  0.000277  0.000107  0.000078  0.000095
GOOG  0.000107  0.000251  0.000078  0.000108
IBM   0.000078  0.000078  0.000146  0.000089
MSFT  0.000095  0.000108  0.000089  0.000215

使用 DataFrame 的corrwith方法,您可以计算 DataFrame 的列或行与另一个 Series 或 DataFrame 之间的成对相关性。传递一个 Series 会返回一个 Series,其中计算了每列的相关值:

In [287]: returns.corrwith(returns["IBM"])
Out[287]: 
AAPL    0.386817
GOOG    0.405099
IBM     1.000000
MSFT    0.499764
dtype: float64

传递一个 DataFrame 会计算匹配列名的相关性。在这里,我计算了百分比变化与成交量的相关性:

In [288]: returns.corrwith(volume)
Out[288]: 
AAPL   -0.075565
GOOG   -0.007067
IBM    -0.204849
MSFT   -0.092950
dtype: float64

传递axis="columns"会逐行执行操作。在所有情况下,在计算相关性之前,数据点都会按标签对齐。

唯一值、值计数和成员资格

另一类相关方法提取一维 Series 中包含的值的信息。为了说明这些方法,考虑以下示例:

In [289]: obj = pd.Series(["c", "a", "d", "a", "a", "b", "b", "c", "c"])

第一个函数是unique,它为您提供 Series 中唯一值的数组:

In [290]: uniques = obj.unique()
In [291]: uniques
Out[291]: array(['c', 'a', 'd', 'b'], dtype=object)

唯一的值不一定按它们首次出现的顺序返回,也不按排序顺序返回,但如果需要的话可以在之后排序(uniques.sort())。相关地,value_counts计算包含值频率的 Series:

In [292]: obj.value_counts()
Out[292]: 
c    3
a    3
b    2
d    1
Name: count, dtype: int64

Series 按值降序排序以方便起见。value_counts也作为顶级 pandas 方法可用,可与 NumPy 数组或其他 Python 序列一起使用:

In [293]: pd.value_counts(obj.to_numpy(), sort=False)
Out[293]: 
c    3
a    3
d    1
b    2
Name: count, dtype: int64

isin执行矢量化的成员检查,并且在将数据集过滤到 Series 或 DataFrame 中的值子集时可能很有用:

In [294]: obj
Out[294]: 
0    c
1    a
2    d
3    a
4    a
5    b
6    b
7    c
8    c
dtype: object
In [295]: mask = obj.isin(["b", "c"])
In [296]: mask
Out[296]: 
0     True
1    False
2    False
3    False
4    False
5     True
6     True
7     True
8     True
dtype: bool
In [297]: obj[mask]
Out[297]: 
0    c
5    b
6    b
7    c
8    c
dtype: object

isin相关的是Index.get_indexer方法,它从可能不同的值的数组中为另一个不同值的数组提供索引数组:

In [298]: to_match = pd.Series(["c", "a", "b", "b", "c", "a"])
In [299]: unique_vals = pd.Series(["c", "b", "a"])
In [300]: indices = pd.Index(unique_vals).get_indexer(to_match)
In [301]: indices
Out[301]: array([0, 2, 1, 1, 0, 2])

有关这些方法的参考,请参见表 5.9。

表 5.9:唯一值、值计数和成员资格方法

方法 描述
isin 计算一个布尔数组,指示每个 Series 或 DataFrame 值是否包含在传递的值序列中
get_indexer 为数组中的每个值计算整数索引,以便将其对齐到另一个不同值的数组;有助于数据对齐和连接类型操作
unique 计算 Series 中唯一值的数组,按观察顺序返回
value_counts 返回一个 Series,其唯一值作为索引,频率作为值,按降序计数排序

在某些情况下,您可能希望在 DataFrame 中的多个相关列上计算直方图。以下是一个示例:

In [302]: data = pd.DataFrame({"Qu1": [1, 3, 4, 3, 4],
 .....:                      "Qu2": [2, 3, 1, 2, 3],
 .....:                      "Qu3": [1, 5, 2, 4, 4]})
In [303]: data
Out[303]: 
 Qu1  Qu2  Qu3
0    1    2    1
1    3    3    5
2    4    1    2
3    3    2    4
4    4    3    4

我们可以计算单列的值计数,如下所示:

In [304]: data["Qu1"].value_counts().sort_index()
Out[304]: 
Qu1
1    1
3    2
4    2
Name: count, dtype: int64

要为所有列计算此值,请将pandas.value_counts传递给 DataFrame 的apply方法:

In [305]: result = data.apply(pd.value_counts).fillna(0)
In [306]: result
Out[306]: 
 Qu1  Qu2  Qu3
1  1.0  1.0  1.0
2  0.0  2.0  1.0
3  2.0  2.0  0.0
4  2.0  0.0  2.0
5  0.0  0.0  1.0

在这里,结果中的行标签是所有列中出现的不同值。这些值是每列中这些值的相应计数。

还有一个DataFrame.value_counts方法,但它计算考虑 DataFrame 的每一行作为元组的计数,以确定每个不同行的出现次数:

In [307]: data = pd.DataFrame({"a": [1, 1, 1, 2, 2], "b": [0, 0, 1, 0, 0]})
In [308]: data
Out[308]: 
 a  b
0  1  0
1  1  0
2  1  1
3  2  0
4  2  0
In [309]: data.value_counts()
Out[309]: 
a  b
1  0    2
2  0    2
1  1    1
Name: count, dtype: int64

在这种情况下,结果具有一个表示不同行的索引作为层次索引,这是我们将在第八章:数据整理:连接、合并和重塑中更详细地探讨的一个主题。

5.4 结论

在下一章中,我们将讨论使用 pandas 读取(或加载)和写入数据集的工具。之后,我们将深入探讨使用 pandas 进行数据清洗、整理、分析和可视化的工具。


相关文章
|
Python 数据可视化 索引
Python 数据分析(PYDA)第三版(四)(2)
Python 数据分析(PYDA)第三版(四)
25 0
Python 数据分析(PYDA)第三版(四)(2)
|
15天前
|
数据挖掘 Python 索引
Python 数据分析(PYDA)第三版(一)(3)
Python 数据分析(PYDA)第三版(一)
37 0
Python 数据分析(PYDA)第三版(一)(3)
|
15天前
|
存储 数据挖掘 Python
Python 数据分析(PYDA)第三版(一)(2)
Python 数据分析(PYDA)第三版(一)
149 0
Python 数据分析(PYDA)第三版(一)(2)
|
15天前
|
数据挖掘 Python 存储
Python 数据分析(PYDA)第三版(三)(2)
Python 数据分析(PYDA)第三版(三)
86 0
|
15天前
|
Python 索引 数据挖掘
Python 数据分析(PYDA)第三版(四)(1)
Python 数据分析(PYDA)第三版(四)
71 0
|
15天前
|
数据挖掘 Python 存储
Python 数据分析(PYDA)第三版(一)(4)
Python 数据分析(PYDA)第三版(一)
29 0
|
15天前
|
存储 Python 索引
Python 数据分析(PYDA)第三版(五)(3)
Python 数据分析(PYDA)第三版(五)
62 0
|
15天前
|
索引 Python 数据挖掘
Python 数据分析(PYDA)第三版(七)(1)
Python 数据分析(PYDA)第三版(七)
35 0
Python 数据分析(PYDA)第三版(七)(1)
|
15天前
|
Python 数据挖掘 索引
Python 数据分析(PYDA)第三版(五)(2)
Python 数据分析(PYDA)第三版(五)
62 0
|
15天前
|
索引 Python 数据挖掘
Python 数据分析(PYDA)第三版(五)(1)
Python 数据分析(PYDA)第三版(五)
27 0
Python 数据分析(PYDA)第三版(五)(1)