AI工程师修炼之路:NumPy、Pandas与Matplotlib实战

5

人工智能(AI)正在深刻地改变着我们的生活和工作方式。作为一名有志于投身AI领域的学习者,仅仅追逐高薪和热门技术是不够的。我们需要对人工智能领域有一个全面而深刻的理解,形成自己的判断,才能做出明智的选择,并在这个快速发展的领域中找到自己的位置。本文将以NumPy、Pandas和Matplotlib这三大Python数据科学库为基石,结合详实的案例和图表,为你构建一套系统化的AI知识体系,助你成为一名合格的AI工程师。

一、人工智能概览

人工智能并非遥不可及的未来科技,而是已经渗透到我们日常生活中的方方面面。从语音识别、机器翻译到图像识别、智能风控,AI的身影无处不在。一个经典的AI定义是:“智能主体可以理解数据及从中学习,并利用知识实现特定目标和任务的能力。”

在技术层面,机器学习是目前人工智能的核心技术。机器学习是指非显式的计算机程序可以从数据中学习,以此提高处理任务的水平。常见的任务包括分类(例如,使用逻辑回归模型判断邮件是否为垃圾邮件)和回归预测(例如,使用线性回归模型预测房价)。深度学习作为机器学习的一个子方向,通过搭建深层的神经网络模型来处理复杂任务,是当前的热门技术。

从应用领域来看,人工智能在语言识别、自然语言处理、图像识别、数据挖掘、推荐系统、智能风控、机器人等众多领域都有着广泛的应用。值得注意的是,不同应用领域在技术层面虽然具有一定的共通性,但在实际应用场景中,对业务知识、算法和工程的要求却存在相当大的差异。

二、基础知识与工具准备

要进入人工智能领域,扎实的编程和数学基础是必不可少的。AI算法工程师首先是一名程序员,掌握编程实现方法才能将理论知识转化为实际应用。而数学则是人工智能理论的基石,对算法原理的理解和进阶至关重要。

在人工智能领域,Python已经成为最受欢迎的编程语言之一,原因如下:

  • 语法简单易懂,适合零基础入门。
  • 拥有丰富的机器学习库,方便机器学习的开发。
  • 在机器学习领域有较高的使用率,意味着社区庞大,应用范围广,市场上有较多的工作机会。

在数学方面,初学者如果数学基础比较薄弱,可以先补习“数学的最小必要知识”,例如线性代数的矩阵运算、高等数学的梯度求导、概率的条件概率和贝叶斯定理等。这些知识可以帮助理解大部分的算法。

三、NumPy科学计算库

NumPy(Numerical Python)是Python中用于数值计算的基石。它提供了一个强大的N维数组对象,以及用于处理这些数组的各种函数。NumPy的高效数组运算能力使其成为数据分析和科学计算的首选工具。

  1. 数组创建:NumPy提供了多种创建数组的方法,例如:
    • 将Python列表转换为NumPy数组:

      import numpy as np
      l = [1, 3, 5, 7, 9]
      arr = np.array(l)
      print(arr)
    • 使用内置函数创建数组:

      arr1 = np.ones(10)  # 创建包含10个1的数组
      arr2 = np.zeros(10)  # 创建包含10个0的数组
      arr3 = np.full(shape=[2, 3], fill_value=2.718) # 创建一个2x3的数组,所有元素都为2.718
      arr4 = np.arange(start=0, stop=20, step=2) # 创建一个等差数列数组,从0到20,步长为2
      arr5 = np.linspace(start=0, stop=9, num=10) # 创建一个等差数列数组,从0到9,包含10个元素
      arr6 = np.random.randint(0, 100, size=10) # 创建一个包含10个0到100之间随机整数的数组
      arr7 = np.random.randn(5) # 创建一个包含5个符合正态分布的随机数的数组
      arr8 = np.random.random(size=5) # 创建一个包含5个0到1之间随机浮点数的数组
  2. 数组属性查看:NumPy数组具有多种属性,可以帮助我们了解数组的结构和数据类型:
    • ndim:数组的轴数(维度)。
    • size:数组元素的总数。
    • dtype:数组元素的数据类型。
    • itemsize:数组中每个元素的大小(以字节为单位)。
  3. 文件IO操作:NumPy允许我们将数组保存到文件中,并在需要时重新加载:
    • 保存数组:

      x = np.random.randn(5)
      y = np.arange(0, 10, 1)
      np.save("x_arr", x) # 将数组x保存到x_arr.npy文件中
      np.savez("some_array.npz", xarr=x, yarr=y) # 将数组x和y保存到some_array.npz文件中
    • 读取数组:

      np.load('x_arr.npy') # 从x_arr.npy文件中加载数组
      np.load('some_array.npz')['yarr'] # 从some_array.npz文件中加载数组y
    • 读写csv、txt文件:

      arr = np.random.randint(0, 10, size=(3, 4))
      np.savetxt("arr.csv", arr, delimiter=',') # 将数组保存到arr.csv文件中,使用逗号作为分隔符
      np.loadtxt("arr.csv", delimiter=',', dtype=np.int32) # 从arr.csv文件中加载数组,使用逗号作为分隔符,数据类型为int32
  4. 数据类型:NumPy支持多种数据类型,包括整数、浮点数和字符串。我们可以使用dtype参数在创建数组时指定数据类型,也可以使用astype方法进行数据类型转换。
  5. 数组运算:NumPy允许我们对数组进行各种算术运算和逻辑运算。这些运算可以逐元素地进行,也可以将标量值传播到整个数组。
  6. 数组的赋值,浅拷贝,深拷贝:理解NumPy数组的赋值、浅拷贝和深拷贝对于避免意外修改数据至关重要。
    • 赋值操作只是创建了一个指向原始数组的引用,修改赋值后的数组会影响原始数组。
    • 浅拷贝创建了一个新的数组对象,但新数组和原始数组共享相同的数据。修改浅拷贝后的数组会影响原始数组。
    • 深拷贝创建了一个完全独立的数组对象,新数组和原始数组互不影响。
  7. 索引、切片和迭代:NumPy提供了灵活的索引和切片机制,可以方便地访问和修改数组中的元素。我们还可以使用迭代器来遍历数组中的所有元素。
  8. 形状操作:NumPy提供了多种形状操作方法,可以改变数组的形状、转置数组、堆叠数组和拆分数组。
  9. 广播机制:当两个数组的形状不兼容时,NumPy会尝试通过广播机制将它们扩展到相同的形状,然后再进行运算。广播机制可以简化代码,提高效率。
  10. 通用函数:NumPy提供了大量的通用函数,可以对数组进行各种数学运算、排序、集合运算和统计运算。
  11. 线性代数:NumPy的numpy.linalg模块提供了丰富的线性代数函数,可以进行矩阵乘积、求逆、行列式、特征值和特征向量等运算。

四、pandas数据分析库

pandas是Python中用于数据分析的核心库。它提供了快速、灵活和明确的数据结构,旨在简化和加速数据处理过程。

  1. 数据结构:pandas主要有两种数据结构:
    • Series:一维带标签的数组,类似于NumPy的数组,但具有更强大的索引功能。
    • DataFrame:二维表格型数据结构,类似于SQL表格或Excel表格,可以存储多种数据类型的数据。
  2. 数据查看:pandas提供了多种方法来查看DataFrame的属性、概览和统计信息:
    • head(n):显示DataFrame的前n行,默认为5行。
    • tail(n):显示DataFrame的后n行,默认为5行。
    • shape:查看DataFrame的形状,返回行数和列数。
    • dtypes:查看DataFrame各列的数据类型。
    • index:查看DataFrame的行索引。
    • columns:查看DataFrame的列索引。
    • values:查看DataFrame的值,返回一个二维ndarray数组。
    • describe():查看DataFrame数值型列的汇总统计信息,包括计数、平均值、标准差、最小值、四分位数和最大值。
    • info():查看DataFrame的列索引、数据类型、非空计数和内存信息。
  3. 数据输入与输出:pandas可以从多种数据源读取数据,并将数据写入到多种格式的文件中:
    • CSV:使用read_csv函数读取CSV文件,使用to_csv方法将DataFrame写入CSV文件。
    • Excel:使用read_excel函数读取Excel文件,使用to_excel方法将DataFrame写入Excel文件。
    • SQL:使用read_sql函数从SQL数据库读取数据,使用to_sql方法将DataFrame写入SQL数据库。
    • HDF5:使用read_hdf函数读取HDF5文件,使用to_hdf方法将DataFrame写入HDF5文件。
  4. 数据选取:pandas提供了多种数据选取方法,可以根据字段、标签、位置和布尔索引来选择数据。
  5. 数据集成:pandas提供了多种将Series和DataFrame对象组合在一起的功能,包括concat、append和merge。
  6. 数据清洗:pandas提供了多种数据清洗方法,可以处理重复数据、空数据和异常值。
  7. 数据转换:pandas提供了多种数据转换方法,可以替换轴和元素、应用函数和进行重排随机抽样。
  8. 数据重塑:pandas提供了多种数据重塑方法,可以转置DataFrame、堆叠和取消堆叠数据。
  9. 数学和统计方法:pandas提供了大量的数学和统计方法,可以计算简单统计指标、索引标签、位置获取、更多统计指标和高级统计指标。
  10. 数据排序:pandas提供了多种数据排序方法,可以根据索引列名或属性值进行排序。
  11. 分箱操作:pandas提供了分箱操作,可以将连续数据转换为分类数据。
  12. 分组聚合:pandas提供了强大的分组聚合功能,可以根据一个或多个列对数据进行分组,并对每个组应用聚合函数。
  13. 时间序列:pandas提供了强大的时间序列分析功能,可以进行时间戳操作、时间戳索引和常用时间序列方法。

五、Matplotlib数据可视化

Matplotlib是Python中最常用的绘图库,可以创建各种静态、动态和交互式图表。

  1. 基础知识:Matplotlib的基本绘图流程包括:
    • 创建图形(Figure)和轴域(Axes)。
    • 在轴域上绘制各种图形,例如折线图、散点图、柱状图等。
    • 设置坐标轴刻度、标签和标题。
    • 添加图例。
    • 移动脊柱。
    • 保存图片。
  2. 风格和样式:Matplotlib允许我们自定义图表的风格和样式,包括颜色、线型、点形、线宽和透明度。
  3. 多图布局:Matplotlib提供了多种多图布局方式,包括子视图、嵌套和多图布局分格显示。
  4. 文本、注释、箭头:Matplotlib允许我们在图表中添加文本、注释和箭头,以增强图表的可读性。
  5. 常用视图:Matplotlib支持多种常用视图,包括折线图、柱状图、极坐标图、直方图、箱形图、散点图、饼图、甜甜圈、热力图、面积图和蜘蛛图。
  6. 3D图形:Matplotlib可以创建简单的3D图形,包括三维折线图散点图和三维柱状图。

六、案例实战:数据分析师招聘数据分析

本文将以一个数据分析师招聘数据分析案例来演示如何使用Matplotlib进行数据可视化。

  1. 各城市对数据分析岗位的需求量

    plt.figure(figsize=(12, 9))
    cities = job['city'].value_counts()
    plt.barh(y=cities.index[::-1],
             width=cities.values[::-1],
             color='#3c7f99')
    plt.box(False)
    plt.title(label=' 各城市数据分析岗位的需求量 ', fontsize=32, weight='bold', color='white', backgroundcolor='#c5b783', pad=30)
    plt.tick_params(labelsize=16)
    plt.grid(axis='x', linewidth=0.5, color='#3c7f99')
    plt.show()
  2. 不同领域对数据分析岗的需求量

    industry_index = job["industryField"].value_counts()[:10].index
    industry = job.loc[job["industryField"].isin(industry_index), "industryField"]
    plt.figure(figsize=(12, 9))
    plt.barh(y=industry_index[::-1],
             width=pd.Series.value_counts(industry.values).values[::-1],
             color='#3c7f99')
    plt.title(label=' 细分领域数据分析岗位的需求量(取前十) ', fontsize=32, weight='bold', color='white', backgroundcolor='#c5b783', ha='center', pad=30)
    plt.tick_params(labelsize=16)
    plt.grid(lw=0.5, color='#3c7f99', ls='--')
    plt.show()
  3. 各城市薪资状况

    plt.figure(figsize=(12, 9))
    city_salary = job.groupby("city")["salary"].mean().sort_values()
    plt.bar(x=city_salary.index, height=city_salary.values,
            color=plt.cm.RdBu_r(np.linspace(0, 1, len(city_salary))))
    plt.title(label=' 各城市的薪资水平对比 ', fontsize=32, weight='bold', color='white', backgroundcolor='#3c7f99')
    plt.tick_params(labelsize=16)
    plt.grid(axis='y', linewidth=0.5, color='black')
    plt.yticks(ticks=np.arange(0, 25, step=5), labels=['', '5k', '10k', '15k', '20k'])
    plt.box(False)
    plt.show()
  4. 工作经验与薪水关系

    work_salary = job.pivot_table(index="city", columns="workYear", values="salary")
    work_salary = work_salary[["应届毕业生", "1-3年", "3-5年", "5-10年"]].sort_values(by='5-10年', ascending=False)
    data = work_salary.values
    data = np.repeat(data, 4, axis=1)
    plt.figure(figsize=(12, 9))
    plt.imshow(data, cmap='RdBu_r')
    plt.yticks(np.arange(13), work_salary.index)
    plt.xticks(np.array([1.5, 5.5, 9.5, 13.5]), work_salary.columns)
    h, w = data.shape
    for x in range(w):
        for y in range(h):
            if (x % 4 == 0) and (~np.isnan(data[y, x])):
                text = plt.text(x + 1.5, y, round(data[y, x], 1),
                               ha="center", va="center", color='r', fontsize=16)
    plt.colorbar(shrink=0.85)
    plt.tick_params(labelsize=16)
    plt.show()
  5. 学历要求

    education = job["education"].value_counts(normalize=True)
    plt.figure(figsize=(9, 9))
    _ = plt.pie(education, labels=education.index, autopct='%0.2f%%',
                wedgeprops=dict(linewidth=3, width=0.5), pctdistance=0.8,
                textprops=dict(fontsize=20))
    _ = plt.title(label=' 学历要求 ', fontsize=32, weight='bold',
                  color='white', backgroundcolor='#c5b783')
    plt.show()
  6. 技能要求

    def get_level(x):
        if x["Python/R"] == 1:
            x["skill"] = "Python/R"
        elif x["SQL"] == 1:
            x["skill"] = "SQL"
        elif x["Excel"] == 1:
            x["skill"] = "Excel"
        elif x['SPSS/SAS'] == 1:
            x['skill'] = 'SPSS/SAS'
        else:
            x["skill"] = "其他"
        return x
    
    
    job = job.apply(get_level, axis=1)
    x = job.loc[job.skill != '其他'][['salary', 'skill']]
    cond1 = x['skill'] == 'Python/R'
    cond2 = x['skill'] == 'SQL'
    cond3 = x['skill'] == 'Excel'
    cond4 = x['skill'] == 'SPSS/SAS'
    plt.figure(figsize=(12, 8))
    plt.title(label=' 不同技能的薪资水平对比 ', fontsize=32, weight='bold', color='white',
              backgroundcolor='#c5b783', pad=30)
    plt.boxplot(x=[job.loc[job.skill != '其他']['salary'][cond1],
                    job.loc[job.skill != '其他']['salary'][cond2],
                    job.loc[job.skill != '其他']['salary'][cond3],
                    job.loc[job.skill != '其他']['salary'][cond4]],
                vert=False, labels=["Python/R", "SQL", "Excel", 'SPSS/SAS'])
    plt.tick_params(axis="both", labelsize=16)
    plt.grid(axis='x', linewidth=0.75)
    plt.xticks(np.arange(0, 61, 10), [str(i) + "k" for i in range(0, 61, 10)])
    plt.box(False)
    plt.xlabel('工资', fontsize=18)
    plt.ylabel('技能', fontsize=18)
    plt.show()
  7. 大公司对技能要求

    skill_count = job[job['companySize'] == '2000人以上'][['Python', 'SQL', 'Tableau', 'Excel', 'SPSS/SAS']].sum()
    plt.figure(figsize=(9, 6))
    plt.bar(np.arange(5), skill_count,
            tick_label=['Python/R', 'SQL', 'Tableau', 'Excel', 'SPSS/SAS'],
            width=0.5,
            color=plt.cm.RdBu_r(skill_count / skill_count.max()))
    _ = plt.title(label=' 大公司对技能的要求 ', fontsize=32, weight='bold', color='white',
                  backgroundcolor='#c5b783', pad=30)
    plt.tick_params(labelsize=16, )
    plt.grid(axis='y')
    plt.box(False)
    plt.show()
  8. 不同规模的公司在招人要求上的差异

    from matplotlib import gridspec
    
    workYear_map = {
        "5-10年": 5,
        "3-5年": 4,
        "1-3年": 3,
        "1年以下": 2,
        "应届毕业生": 1}
    color_map = {
        5: "#ff0000",
        4: "#ffa500",
        3: "#c5b783",
        2: "#3c7f99",
        1: "#0000cd"}
    cond = job.workYear.isin(workYear_map)
    job = job[cond]
    job['workYear'] = job.workYear.map(workYear_map)
    job['companySize'] = job['companySize'].astype('category')
    list_custom = ['2000人以上', '500-2000人', '150-500人', '50-150人', '15-50人', '少于15人']
    job['companySize'].cat.reorder_categories(list_custom, inplace=True)
    job.sort_values(by='companySize', inplace=True, ascending=False)
    plt.figure(figsize=(12, 11))
    gs = gridspec.GridSpec(10, 1)
    plt.subplot(gs[:8])
    plt.suptitle(t=' 不同规模公司的用人需求差异 ', fontsize=32,
                 weight='bold', color='white', backgroundcolor='#3c7f99')
    plt.scatter(job.salary, job.companySize,
                c=job.workYear.map(color_map),
                s=(job.workYear * 100), alpha=0.35)
    plt.scatter(job.salary, job.companySize,
                c=job.workYear.map(color_map))
    plt.grid(axis='x')
    plt.xticks(np.arange(0, 161, 10), [str(i) + "k" for i in range(0, 161, 10)])
    plt.xlabel('工资', fontsize=18)
    plt.box(False)
    plt.tick_params(labelsize=18)
    plt.subplot(gs[9:])
    x = np.arange(5)[::-1]
    y = np.zeros(len(x))
    s = x * 100
    plt.scatter(x, y, s=s, c=color_map.values(), alpha=0.3)
    plt.scatter(x, y, c=color_map.values())
    plt.box(False)
    plt.xticks(ticks=x, labels=list(workYear_map.keys()), fontsize=14)
    plt.yticks(np.arange(1), labels=[' 经验:'], fontsize=18)
    plt.show()

七、总结

NumPy、Pandas和Matplotlib是Python数据科学的三大基石。掌握这三大库,你就可以进行高效的数据处理、数据分析和数据可视化。结合实际案例,可以让你更好地理解和运用这些知识,为进入人工智能领域打下坚实的基础。