Python 是数据科学领域中最流行的语言,因其简单性、庞大的社区和大量的开源库而受到广泛使用。
如果你正在进行数据科学项目,Python 的包将会使你的工作变得更加轻松,因为你只需要几行代码就可以完成复杂的操作,如数据操作和应用机器学习/深度学习模型。
在开始你的数据科学之旅时,建议先学习两个最有用的 Python 包:NumPy 和 Pandas。本文将介绍这两个库。让我们开始吧!
NumPy 是什么?
NumPy 是 Numerical Python 的缩写,它用于在机器学习模型的背后进行高效的数组和矩阵计算。NumPy 的基本构建块是数组,它是一个类似于列表的数据结构,但提供了大量的数学函数。换句话说,NumPy 数组是一个多维数组对象。
创建 NumPy 数组
我们可以使用列表或列表的列表来定义 NumPy 数组:
import numpy as np
l = [[1,2,3],[4,5,6],[7,8,9]]
numpy_array = np.array(l)
numpy_array
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
与列表的列表不同,我们可以使用缩进来可视化 3X3 的矩阵。此外,NumPy 还提供了40多个内置函数用于创建数组。
要创建一个填充为零的数组,可以使用 np.zeros 函数,只需要指定所需的形状:
zeros_array = np.zeros((3,4))
zeros_array
array([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
同样地,我们可以创建一个填充为 1 的数组:
ones_array = np.ones((3,4))
ones_array
array([[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]])
我们还可以创建单位矩阵,它是一个主对角线上元素为 1,副对角线上元素为 0 的方阵:
identity_array = np.identity(3)
identity_array
array([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])
此外,NumPy 还提供了不同的函数来创建随机数组。要创建一个从均匀分布 [0,1] 中随机取样的数组,只需要使用函数 np.random.rand :
random_array = np.random.rand(3,4)
random_array
array([[0.84449279, 0.71146992, 0.48159787, 0.04927379],
[0.03428534, 0.26851667, 0.65718662, 0.52284251],
[0.1380207 , 0.91146148, 0.74171469, 0.57325424]])
与前一个函数类似,我们可以定义一个带有随机值的数组,但这次是从标准正态分布中取样:
randn_array = np.random.randn(10)
randn_array
array([-0.68398432, -0.25466784, 0.27020797, 0.29632334, -0.20064897,
0.7988508 , 1.34759319, -0.41418478, -0.35223377, -0.10282884])
如果我们有兴趣构建一个随机整数数组,这些整数属于区间[low,high),我们只需要使用np.random.randint函数:
randint_array = np.random.randint(1,20,20)
randint_array
array([14, 3, 1, 2, 17, 15, 5, 17, 18, 9, 4, 19, 14, 14, 1, 10, 17,
19, 4, 6])
索引和切片
除了用于数组创建的内置函数之外,NumPy的另一个优点是可以使用一组方括号从数组中选择元素。例如,我们可以尝试获取矩阵的第一行:
a1 = np.array([[1,2,3],[4,5,6]])
a1[0]
array([1, 2, 3])
假设我们想选择第一行的第三个元素。在这种情况下,我们需要指定两个索引,即行索引和列索引:
print(a1[0,2]) #3
另一种选择是使用a1[0][2],但这被认为是低效的,因为它首先创建包含第一行的数组,然后从该行中选择元素。
此外,我们可以使用方括号内的语法start:stop:step从矩阵中取出切片,其中停止索引不包含在内。例如,我们再次想选择第一行,但只取前两个元素:
print(a1[0,0:2])
[1 2]
如果我们想选择所有行,但只提取每行的第一个元素:
print(a1[:,0])
[1 4]
除了整数数组索引之外,还有布尔数组索引来从数组中选择元素。假设我们只想选择满足以下条件的元素:
a1>5
array([[False, False, False],
[False, False, True]])
如果我们根据这个条件过滤数组,输出将只显示True元素:
a1[a1>5]
array([6])
数组操作
在进行数据科学项目时,经常需要将数组重新形状为新的形状而不改变数据。
例如,我们从一个维度为2X3的数组开始。如果我们不确定数组的形状,可以使用shape属性来帮助我们:
a1 = np.array([[1,2,3],[4,5,6]])
print(a1)
print('数组形状:',a1.shape)
[[1 2 3]
[4 5 6]]
数组形状: (2, 3)
要将数组重新形状为3X2的维度,我们可以简单地使用reshape函数:
a1 = a1.reshape(3,2)
print(a1)
print('数组形状: ',a1.shape)
[[1 2]
[3 4]
[5 6]]
数组形状: (3, 2)
另一个常见的情况是将多维数组转换为一维数组。可以通过指定-1作为形状来实现:
a1 = a1.reshape(-1)
print(a1)
print('数组形状: ',a1.shape)
[1 2 3 4 5 6]
数组形状: (6,)
有时候需要获得转置后的数组:
a1 = np.array([[1,2,3,4,5,6]])
print('转置前数组形状: ',a1.shape)
a1 = a1.T
print(a1)
print('转置后数组形状: ',a1.shape)
转置前数组形状: (1, 6)
[[1]
[2]
[3]
[4]
[5]
[6]]
转置后数组形状: (6, 1)
同样,也可以使用np.transpose(a1)来实现相同的转置。
数组乘法
如果要从头开始构建机器学习算法,你肯定需要计算两个数组的矩阵乘积。当数组维度大于1时,可以使用函数np.matmul来实现:
a1 = np.array([[1,2,3],[4,5,6]])
a2 = np.array([[1,2],[4,5],[7,8]])
print('数组a1的形状: ',a1.shape)
print('数组a2的形状: ',a2.shape)
a3 = np.matmul(a1,a2)
# a3 = a1 @ a2
print(a3)
print('数组a3的形状: ',a3.shape)
数组a1的形状: (2, 3)
数组a2的形状: (3, 2)
[[30 36]
[66 81]]
数组a3的形状: (2, 2)
@可以作为np.matmul的简洁替代方式。
如果要将矩阵乘以标量,np.dot是最佳选择:
a1 = np.array([[1,2,3],[4,5,6]])
a3 = np.dot(a1,2)
# a3 = a1 * 2
print(a3)
print('数组a3的形状: ',a3.shape)
[[ 2 4 6]
[ 8 10 12]]
数组a3的形状: (2, 3)
在这种情况下,*可以作为np.dot的简洁替代方式。
数学函数
NumPy提供了大量的数学函数,例如三角函数、取整函数、指数函数、对数函数等等。你可以在这里找到完整列表。我们将展示一些最重要的函数,你可以应用到你的问题中。
指数函数和自然对数函数无疑是最常见和最常用的转换:
a1 = np.array([[1,2,3],[4,5,6]])
print(np.exp(a1))
[[ 2.71828183 7.3890561 20.08553692]
[ 54.59815003 148.4131591 403.42879349]]
a1 = np.array([[1,2,3],[4,5,6]])
print(np.log(a1))
[[0. 0.69314718 1.09861229]
[1.38629436 1.60943791 1.79175947]]
如果我们想要在单行代码中提取最小值和最大值,我们只需要调用以下函数:
a1 = np.array([[1,2,3],[4,5,6]])
print(np.min(a1),np.max(a1)) # 1 6
我们还可以计算数组中每个元素的平方根:
a1 = np.array([[1,2,3],[4,5,6]])
print(np.sqrt(a1))
[[1. 1.41421356 1.73205081]
[2. 2.23606798 2.44948974]]
什么是Pandas?
Pandas建立在Numpy上,用于操作数据集。主要有两种数据结构:Series和Dataframe。Series是一个值的序列,而Dataframe是一个带有行和列的表格。换句话说,Series是Dataframe的一列。
创建Series和Dataframe
要构建Series,我们只需将值列表传递给方法:
import pandas as pd
type_house = pd.Series(['Loft','Villa'])
type_house
0 Loft
1 Villa
dtype: object
我们可以通过传递对象字典来创建Dataframe,其中键对应于列名,值是列的条目:
df = pd.DataFrame({'Price': [100000, 300000], 'date_construction': [1960, 2010]})
df.head()
创建Dataframe后,我们可以检查每列的类型:
type(df.Price),type(df.date_construction)
(pandas.core.series.Series, pandas.core.series.Series)
很明显,列是Series类型的数据结构。
汇总函数
从现在开始,我们将使用Kaggle上提供的自行车共享数据集展示Pandas的潜力。我们可以以以下方式导入CSV文件:
df = pd.read_csv('/kaggle/input/bike-sharing-demand/train.csv')
df.head()
Pandas不仅允许读取CSV文件,还可以读取Excel文件、JSON、Parquet和其他类型的文件。您可以在这里找到完整列表。
从输出中,我们可以可视化数据框的前五行。如果我们想要显示数据集的最后四行,我们可以使用tail()方法:
df.tail(4)
几行数据不足以对我们拥有的数据有一个好的了解。开始分析的一个好方法是查看数据集的形状:
df.shape #(10886, 12)
我们有10886行和12列。您想要查看列名吗?这非常直观:
df.columns
有一种方法可以将所有这些信息可视化到一个输出中:
df.info()
如果我们想要显示每列的统计信息,可以使用describe方法:
df.describe()
从分类字段中提取信息也很重要。我们可以找到season列的唯一值和唯一值的数量:
df.season.unique(),df.season.nunique()
输出:
(array([1, 2, 3, 4]), 4)
我们可以看到值为1, 2, 3, 4。因此,有四个可能的值。这个验证对于理解分类变量并防止列中可能包含的噪声非常重要。
要显示每个级别的频率,可以使用value_counts()方法:
df.season.value_counts()
最后一步应该是检查每列中的缺失值:
df.isnull().sum()
幸运的是,在这些字段中我们没有任何缺失值。
索引和切片
与Numpy类似,有基于索引的选择来从数据结构中选择数据。从数据框中获取条目的主要方法有两种:
- iloc基于整数位置选择元素
- loc基于标签或布尔数组获取元素。
要选择第一行,iloc是最好的选择:
df.iloc[0]
如果我们想要选择所有行和第二列,可以这样做:
df.iloc[:,1]
也可以同时选择多列:
df.iloc[0:3,[0,1,2,5]]
根据索引选择列变得复杂。最好指定列名。可以使用loc来实现:
df.loc[0:3,['datetime','season','holiday','temp']]
与Numpy类似,可以根据条件过滤数据框。例如,我们想要返回所有weather等于1的行:
df[df.weather==1]
如果我们想要返回特定列的输出,可以使用loc:
df.loc[df.weather==1,['season','holiday']]
创建新变量
创建新变量对从数据中提取更多信息和提高可解释性有巨大影响。我们可以根据workingday的值创建一个新的分类变量:
df['workingday_c'] = df['workingday'].apply(lambda x: 'work' if x==1 else 'relax')
df[['workingday','workingday_c']].head()
如果有多个条件,最好使用字典和map方法来映射值:
diz_season = {1:'winter',2:'spring',3:'summer',4:'fall'}
df['season_c'] = df['season'].map(lambda x: diz_season[x])
df[['season','season_c']].head()
分组和排序
有时候你想要根据分类列对数据进行分组。可以使用groupby来实现:
df.groupby('season_c').agg({'count':['median','max']})
对于每个季节的级别,我们可以观察到租赁自行车的中位数和最大值。如果不基于列进行排序,这个输出可能会令人困惑。我们可以使用sort_values()方法来排序:
df.groupby('season_c').agg({'count':['median','max']}).reset_index().sort_values(by=('count', 'median'),ascending=False)
现在,输出更有意义了。我们可以推断出夏天租借自行车的数量最多,而冬天不是一个适合租借自行车的好月份。
最后的想法
就这些!希望您觉得这个指南对学习NumPy和Pandas的基础知识有用。它们通常是分开学习的,但先学习NumPy,然后再学习建立在NumPy之上的Pandas可能更有洞察力。
在教程中,肯定有我没有涵盖的方法,但目标是涵盖这两个库中最重要和最流行的方法。代码可以在Kaggle上找到。谢谢阅读!祝您有愉快的一天! Eugenia Anello目前是意大利帕多瓦大学信息工程系的研究员。她的研究项目专注于连续学习与异常检测的结合。