MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Python通过NumPy进行统计分析

2021-12-136.7k 阅读

NumPy基础

NumPy简介

NumPy(Numerical Python)是Python中最重要的数学计算库之一,它提供了高效的多维数组对象以及一系列用于处理这些数组的函数。对于统计分析而言,NumPy提供了基础的数据结构和操作方法,使得数据处理变得更加便捷和高效。

在使用NumPy之前,需要确保已经安装了它。如果使用的是Anaconda发行版,通常已经默认安装了NumPy。如果没有安装,可以使用pip install numpy命令进行安装。

NumPy数组

NumPy的核心数据结构是ndarray(N-dimensional array),即多维数组。它是一个由相同类型元素组成的多维容器。与Python内置的列表相比,ndarray在存储和计算上都更加高效。

下面是创建ndarray的一些示例:

import numpy as np

# 创建一维数组
arr1d = np.array([1, 2, 3, 4, 5])
print(arr1d)

# 创建二维数组
arr2d = np.array([[1, 2, 3], [4, 5, 6]])
print(arr2d)

在上述代码中,使用np.array()函数将Python列表转换为NumPy数组。可以通过数组的shape属性查看数组的维度和大小:

print(arr1d.shape)  # 输出: (5,)
print(arr2d.shape)  # 输出: (2, 3)

数组索引和切片

NumPy数组的索引和切片操作与Python列表类似,但在多维数组上有更多的灵活性。

对于一维数组,索引和切片方式与列表基本相同:

arr1d = np.array([1, 2, 3, 4, 5])
print(arr1d[0])  # 输出第一个元素: 1
print(arr1d[1:3])  # 输出索引1到2(不包含3)的元素: [2 3]

对于二维数组,需要使用逗号分隔索引来访问不同维度的元素:

arr2d = np.array([[1, 2, 3], [4, 5, 6]])
print(arr2d[0, 0])  # 输出第一行第一列的元素: 1
print(arr2d[1, :])  # 输出第二行所有元素: [4 5 6]
print(arr2d[:, 1])  # 输出所有行的第二列元素: [2 5]

数组运算

NumPy数组支持各种数学运算,这些运算会自动应用到数组的每个元素上,这称为向量化操作。向量化操作避免了显式的循环,大大提高了计算效率。

算术运算

arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])

# 加法
add_result = arr1 + arr2
print(add_result)  # 输出: [5 7 9]

# 乘法
mul_result = arr1 * arr2
print(mul_result)  # 输出: [ 4 10 18]

与标量运算

arr = np.array([1, 2, 3])
scalar = 2

# 乘法
scalar_mul_result = arr * scalar
print(scalar_mul_result)  # 输出: [2 4 6]

NumPy统计函数

基本统计量

求和

np.sum()函数用于计算数组中所有元素的和。对于多维数组,可以通过指定axis参数来计算特定维度的和。

arr1d = np.array([1, 2, 3, 4, 5])
total_sum = np.sum(arr1d)
print(total_sum)  # 输出: 15

arr2d = np.array([[1, 2, 3], [4, 5, 6]])
row_sum = np.sum(arr2d, axis=1)
col_sum = np.sum(arr2d, axis=0)
print(row_sum)  # 输出: [ 6 15]
print(col_sum)  # 输出: [5 7 9]

平均值

np.mean()函数用于计算数组的平均值。同样,axis参数可用于计算特定维度的平均值。

arr1d = np.array([1, 2, 3, 4, 5])
mean_value = np.mean(arr1d)
print(mean_value)  # 输出: 3.0

arr2d = np.array([[1, 2, 3], [4, 5, 6]])
row_mean = np.mean(arr2d, axis=1)
col_mean = np.mean(arr2d, axis=0)
print(row_mean)  # 输出: [ 2.  5.]
print(col_mean)  # 输出: [2.5 3.5 4.5]

中位数

np.median()函数用于计算数组的中位数。

arr1d = np.array([1, 2, 3, 4, 5])
median_value = np.median(arr1d)
print(median_value)  # 输出: 3.0

arr2d = np.array([[1, 2, 3], [4, 5, 6]])
row_median = np.apply_along_axis(np.median, 1, arr2d)
col_median = np.apply_along_axis(np.median, 0, arr2d)
print(row_median)  # 输出: [ 2.  5.]
print(col_median)  # 输出: [2.5 3.5 4.5]

在上述二维数组计算中位数的代码中,使用了np.apply_along_axis()函数,它可以沿着指定的轴对数组应用一个函数。这里是沿着行(axis=1)和列(axis=0)应用np.median()函数。

标准差

np.std()函数用于计算数组的标准差,它反映了数据的离散程度。

arr1d = np.array([1, 2, 3, 4, 5])
std_value = np.std(arr1d)
print(std_value)  # 输出: 1.4142135623730951

arr2d = np.array([[1, 2, 3], [4, 5, 6]])
row_std = np.std(arr2d, axis=1)
col_std = np.std(arr2d, axis=0)
print(row_std)  # 输出: [ 0.81649658  0.81649658]
print(col_std)  # 输出: [1.5 1.5 1.5]

最值相关统计量

最大值和最小值

np.max()np.min()函数分别用于获取数组中的最大值和最小值。对于多维数组,也可以通过axis参数获取特定维度的最值。

arr1d = np.array([1, 2, 3, 4, 5])
max_value = np.max(arr1d)
min_value = np.min(arr1d)
print(max_value)  # 输出: 5
print(min_value)  # 输出: 1

arr2d = np.array([[1, 2, 3], [4, 5, 6]])
row_max = np.max(arr2d, axis=1)
col_min = np.min(arr2d, axis=0)
print(row_max)  # 输出: [3 6]
print(col_min)  # 输出: [1 2 3]

百分位数

百分位数是一种位置指标,np.percentile()函数可以计算数组的指定百分位数。

arr1d = np.array([1, 2, 3, 4, 5])
p25 = np.percentile(arr1d, 25)
p75 = np.percentile(arr1d, 75)
print(p25)  # 输出: 1.75
print(p75)  # 输出: 4.25

在上述代码中,计算了数组的25%和75%百分位数。25%百分位数意味着有25%的数据小于或等于这个值,75%百分位数同理。

其他统计函数

累加和与累乘积

np.cumsum()函数用于计算数组的累加和,np.cumprod()函数用于计算数组的累乘积。

arr1d = np.array([1, 2, 3, 4, 5])
cumulative_sum = np.cumsum(arr1d)
cumulative_product = np.cumprod(arr1d)
print(cumulative_sum)  # 输出: [ 1  3  6 10 15]
print(cumulative_product)  # 输出: [  1   2   6  24 120]

唯一值和计数

np.unique()函数可以获取数组中的唯一值,并可以选择返回每个唯一值的计数。

arr1d = np.array([1, 2, 2, 3, 3, 3])
unique_values, counts = np.unique(arr1d, return_counts=True)
print(unique_values)  # 输出: [1 2 3]
print(counts)  # 输出: [1 2 3]

使用NumPy进行数据处理和统计分析案例

案例一:分析学生成绩

假设我们有一个包含多个班级学生成绩的二维数组,每个班级有若干学生,我们需要对这些成绩进行一些统计分析。

import numpy as np

# 模拟学生成绩数据,每个子列表代表一个班级的成绩
student_scores = np.array([
    [85, 90, 78, 88],
    [92, 88, 95, 80],
    [70, 75, 80, 85]
])

# 计算每个班级的平均成绩
class_averages = np.mean(student_scores, axis=1)
print("每个班级的平均成绩:", class_averages)

# 计算所有学生的平均成绩
overall_average = np.mean(student_scores)
print("所有学生的平均成绩:", overall_average)

# 找出所有成绩中的最高分和最低分
max_score = np.max(student_scores)
min_score = np.min(student_scores)
print("最高分:", max_score)
print("最低分:", min_score)

案例二:销售数据统计

假设我们有一家商店一段时间内不同产品的销售数据,存储在一个二维数组中,每行代表一天的销售数据,每列代表不同的产品。我们要对这些销售数据进行统计分析。

import numpy as np

# 模拟销售数据,每行是一天的销售记录,每列是不同产品
sales_data = np.array([
    [100, 200, 150],
    [120, 180, 160],
    [90, 220, 140]
])

# 计算每天的总销售额
daily_total_sales = np.sum(sales_data, axis=1)
print("每天的总销售额:", daily_total_sales)

# 计算每种产品的总销售额
product_total_sales = np.sum(sales_data, axis=0)
print("每种产品的总销售额:", product_total_sales)

# 计算平均每天的销售额
average_daily_sales = np.mean(daily_total_sales)
print("平均每天的销售额:", average_daily_sales)

# 找出销售额最高的一天
highest_sales_day = np.argmax(daily_total_sales)
print("销售额最高的一天是第", highest_sales_day + 1, "天")

在上述代码中,np.argmax()函数用于返回数组中最大值的索引。因为索引从0开始,所以在输出时加1表示实际的天数。

案例三:股票数据简单分析

假设我们有一段时间内某股票的每日收盘价数据,存储在一维数组中。我们要对这些数据进行一些简单的统计分析。

import numpy as np

# 模拟股票收盘价数据
closing_prices = np.array([100, 105, 103, 108, 102])

# 计算价格的每日变化
price_changes = np.diff(closing_prices)
print("每日价格变化:", price_changes)

# 计算平均价格变化
average_price_change = np.mean(price_changes)
print("平均价格变化:", average_price_change)

# 计算价格变化的标准差
std_price_change = np.std(price_changes)
print("价格变化的标准差:", std_price_change)

在上述代码中,np.diff()函数用于计算数组中相邻元素的差值,这里用于计算股票价格的每日变化。

深入理解NumPy统计分析原理

向量化计算原理

NumPy的高效性很大程度上源于其向量化计算。在传统的Python中,如果要对一个列表中的每个元素进行某种运算,通常需要使用循环:

python_list = [1, 2, 3, 4, 5]
result_list = []
for num in python_list:
    result_list.append(num * 2)
print(result_list)

而在NumPy中,可以直接对数组进行运算:

import numpy as np
np_array = np.array([1, 2, 3, 4, 5])
result_array = np_array * 2
print(result_array)

NumPy的向量化计算是通过底层的C语言实现的。当对ndarray进行运算时,NumPy会将操作广播到数组的每个元素,避免了Python层面的循环,从而大大提高了计算效率。这种广播机制会自动匹配不同形状的数组,只要它们的形状在一定规则下是兼容的。

内存布局与性能优化

ndarray在内存中以连续的块形式存储数据,这使得CPU可以更高效地访问数据。与Python列表不同,列表中的元素可以是不同类型,并且在内存中分布不连续。而ndarray中的元素类型必须相同,这样可以更有效地利用缓存,提高内存访问速度。

另外,NumPy在进行一些操作时,会尽量避免数据的复制,而是通过视图(view)来操作数据。例如,当对数组进行切片时,通常返回的是一个视图,它与原数组共享数据内存,这样可以减少内存占用和提高操作效率。

统计函数的实现细节

np.sum()函数为例,在一维数组的情况下,它会遍历数组中的每个元素并进行累加。对于多维数组,当指定axis参数时,它会沿着指定的轴进行累加操作。在底层实现中,会根据数组的维度和axis参数来确定具体的累加逻辑。

np.mean()函数实际上是在np.sum()的基础上进行计算,先计算总和,再除以元素个数得到平均值。同样,np.std()函数的计算也依赖于基本的算术运算和求和操作。这些统计函数通过高度优化的底层代码实现,使得在处理大规模数据时也能保持高效。

在处理百分位数计算时,np.percentile()函数会先对数组进行排序,然后根据指定的百分位位置计算相应的值。排序算法的选择也会影响计算效率,NumPy通常会使用高效的排序算法来确保性能。

与其他统计分析库的比较

与Pandas的比较

Pandas是另一个常用的Python数据分析库,它基于NumPy构建,提供了更高级的数据结构和数据分析工具。

  • 数据结构:NumPy主要围绕ndarray展开,适用于数值计算。而Pandas的核心数据结构是Series(一维)和DataFrame(二维),DataFrame更适合处理具有标签的数据,比如表格数据,每列可以有不同的数据类型。
  • 功能侧重:NumPy专注于数值计算和数组操作,其统计函数更底层和基础。Pandas则提供了更丰富的数据处理功能,如数据清洗、分组运算、合并等,在数据分析的整个流程中更全面。例如,Pandas的groupby功能可以方便地对数据进行分组统计,而NumPy实现类似功能则相对复杂。

与SciPy的比较

SciPy是一个用于科学计算的Python库,它建立在NumPy之上,包含了许多不同领域的算法和工具,其中scipy.stats模块专注于统计分析。

  • 功能深度:NumPy提供了基本的统计函数,满足常见的统计需求。而scipy.stats模块则提供了更深入和专业的统计功能,例如概率分布函数、假设检验、方差分析等。例如,如果要进行t检验,就需要使用scipy.stats.ttest_ind()函数,NumPy本身不提供这样的功能。
  • 适用场景:NumPy适用于一般的数据处理和基础统计分析。当需要进行更复杂的统计推断、分布拟合等任务时,SciPy则更为合适。

在实际应用中,通常会结合使用NumPy、Pandas和SciPy。NumPy用于高效的数值计算和数组操作,Pandas用于数据处理和清洗,SciPy用于更专业的统计分析。例如,在处理一份包含各种数据类型的数据集时,可能先用Pandas读取和清洗数据,然后将数值部分转换为NumPy数组进行高效计算,最后使用SciPy进行特定的统计检验。

通过深入了解NumPy的统计分析功能,结合与其他相关库的比较,可以在Python编程中更灵活、高效地进行数据处理和统计分析任务。无论是简单的数据分析还是复杂的科学研究,NumPy都为我们提供了坚实的基础。在实际项目中,根据具体需求合理选择和组合这些库,能够大大提高工作效率和分析质量。