python数据分析&可视化

【目录】

1.jupyter

2.pandas

2.数据分析

3.数据展现与报告撰写

4.Apriori算法

5.RFM模型

6.NumPy

7.可视化进阶

8.机器学习导论

(一)Jupyter

(二)pandas

import pandas as pd

1. pandas 库的基本数据结构

1. Series

①组成

Series 主要由一组数据及其对应的索引组成。

②创建

pd.Series() 就是创建 Series 对象的方法。这个方法包含的参数有很多。

​ 主要先讲解一种借助列表来创建 Series 对象的方法,也就是:pd.Series(data)。当我们将列表传给参数 data 之后,在默认情况下,pd.Series(data)会自动为列表中的每一个元素分配对应的数字索引。默认索引是从 0 开始,以 0, 1, 2, 3,… 的形式按序分配给列表中的元素。当然,也可以人为设定 Series 对象的索引,只不过这不是这节课的重点。

输入;

# 创建Series对象
surname = pd.Series(['赵', '钱', '孙', '李'], index = ['学生1', '学生2', '学生3', '学生4'])
# 查看surname
surname

输出:

​ 但其实除了列表以外,还有很多其他的方式可以创建Series:

2.DataFrame
①结构

DataFrame对象是一种表格型的数据结构,包含行索引、列索引以及一组数据。

②创建

​ 创建 DataFrame 对象的方法是:pd.DataFrame()。和创建 Series 对象一样,创建 DataFrame 对象的参数也有很多。这节课会主要讲解一种借助字典来创建 DataFrame 对象的方法,也就是:pd.DataFrame(data)。这里需要将一组数据传给参数 data,数据的形式可以是字典。

​ 不过要注意:各键所对的值的长度如果不相等,就意味着数据是长短不一的,这样的字典如果传给了参数data,则会报错。

​ 方法一:传入字典

# 创建 DataFrame 对象
class_df = pd.DataFrame({'年龄': [25, 18, 23, 18],
                         '性别': ['女', '女', '女', '男']})
# 查看 class_df
class_df

​ 方法二:传入n维数组

# 创建DataFrame对象
import numpy as np
class_df2 = pd.DataFrame(np.array([[25, '女'],
                                   [18, '女'],
                                   [23, '女'],
                                   [18, '男']]),
                         columns = ['年龄', '性别'],
                         index = [1, 2, 3, 4])
# 查看 class_df2
class_df2

​ 方法三:传入多个嵌套列表

goods_info = [
    ['巧克力', 77.675, '47箱', '广东佛山仓'], 
    ['牛奶', 58.755, '40箱', '广东揭阳仓'], 
    ['威化饼干', 7.385, '285盒', '广西南宁仓'], 
    ['火腿肠', 34.823, '20件', '广西柳州仓'], 
    ['巧克力', 62.648, '30箱', '广东揭阳仓'], 
    ['牛奶', 45.75, '12箱', '广西柳州仓'], 
    ['威化饼干', 5.235, '148盒', '广东佛山仓'], 
    ['火腿肠', 33.736, '28件', '广东佛山仓']
]

goods_df = pd.DataFrame(goods_info)
goods_df

观察两个数据的内容,可以发现1.3的区别。

1)以字典作为数据传入时,字典的会作为 DataFrame 对象的列名显示,而字典的会作为对象的列数据显示。

2)以嵌套列表作为数据传入时,列表的元素会作为 DataFrame 对象的行数据显示,且会为数据默认生成从 0 开始的列名。

③列名设置

1 带列名直接创建

# 设置列表
students_info = [['小蓝', '语文', '79'], 
                ['小邱', '数学', '83'], 
                ['小李', '英语', '92']]

# 使用列表创建 DataFrame 对象,同时设置列名
students_df = pd.DataFrame(students_info, columns=['姓名', '科目', '分数'])
students_df

2 补充、修改列名(赋值操作)

# 修改 goods_df 的列名
goods_df.columns = ['名称', '单价', '库存', '地址']
goods_df
3.Series 对象和 DataFrame 对象的联系

DataFrame 对象可以被看作是由 Series 对象所组成的。想要验证这层联系,可以通过 df['列索引'] 这个索引操作来实现。

2.导入数据文件

​ pandas 库为读取各种文件类型的数据提供了非常简便高效的方法。

1.csv

pd.read_csv(path, encoding) 就是其中一种高效读取 csv 文件的方法,返回的是一个 DataFrame 对象。这个方法有很多参数,这里先介绍两种最常用的:pathencoding

​ 对于参数 path,需要传一个文件路径给它。可以是相对路径,也可以是绝对路径。对于参数 encoding,需要传一个文件编码格式给它。文件编码格式的选取需要根据所读文件的编码格式来定。

2.excel

​ 它可以以 DataFrame 格式读取 Excel 文件的数据,并返回一个 DataFrame 对象。

3.部分总结

4.数据预处理

1.数据清洗

首先了解一下查看的方法:

  • 直接输入 DataFrame 对象的名称能查看数据的前后 5 行

  • 可以使用df.head()df.tail()方法查看数据。对DataFrame 对象使用 df.head() 方法默认可以查看数据的前 5 行,df.tail() 方法则默认可以查看数据的后 5 行。当然自己加进去数字,就是前、后几行的意思。

①处理缺失值

**1 查看基本信息 **

​ 在实际的数据分析项目中,面对庞大的数据我们自然不能依赖肉眼的观测。有一个工具能帮助我们提炼出基本的数据信息,从而能让我们对于整体的数据有一个大概的印象。这个方法就是df.info()

当非空数据与数据总量不一致时,说明这份数据有可能存在缺失值,处理这些缺失值的第一步,就是找到它们。

2 查找缺失值

​ 在 pandas 库中,我们可以使用 isna() 方法来查找 DataFrame 对象以及 Series 对象中的缺失值。它可以将查找结果以 DataFrame 对象或者 Series 对象的形式进行返回。df.isna()返回的是 DataFrame 对象,Series.isna() 返回的就是 Series 对象。返回对象中的内容都是布尔值,缺失数据会用 True 来表示,False 则代表这里的数据不缺失。

3 删除缺失值(!记得一定要进行赋值操作,把它重新赋给原来那个)

# 删除所有缺失值
mask_data = mask_data.dropna()
# 查看数据基本信息总结
mask_data.info()

​ 如果我们需要针对某几列的缺失数据进行删除,就需要用到df.dropna()subset 参数。它可以指定 dropna() 方法删除一列或多列数据中含有缺失值的行。我们可以把要指定的列名写进中括号[]中,然后赋值给 subset 参数,就可以限定 dropna() 方法的删除范围。

# 删除'品牌', '上牌时间', '里程数(km)', '保值率'四列数据缺失的行
test_data = test_data.dropna(subset=['品牌', '上牌时间', '里程数(km)', '保值率'])
# 查看 test_data 的基本信息总结
test_data.info()
②处理重复值

1 查找重复值

​ 我们可以直接使用 df.duplicated()(中文意思:重复的)方法来查找 DataFrame 对象中的重复数据。

​ 可以看到,这里的返回结果很长,我们完全没有办法用肉眼进行观察。

​ 我们可以将 mask_data.duplicated() 返回的结果放入中括号 [] 中,用来索引 mask_data 数据,查看 mask_data 数据中重复的行。

2 删除重复行

​ 可以使用 pandas 库的 df.drop_duplicates() 方法直接删除 DataFrame 对象中重复出现的整行数据。

​ 我们能看见 mask_data 的表头下面是空的,说明到这里已经没有重复数据了。

​ 值得一提的是,df.drop_duplicates() 方法并不会将所有重复的行都删除。这里你要弄清重复出现的行与重复的行之间的区别,在 pandas 库中只有重复出现的数据才会被判定为重复数据.,df.drop_duplicates() 方法删除的也都是重复出现的行,因此所有重复数据的第一行都会保留。

######③处理异常值

1 检查异常值

​ 介绍一个检查数据是否存在异常的方法 —— describe()。describe 单词在英语里是描述的意思,而在 pandas 库中 describe() 方法则可以查看 Series 对象或者DataFrame 对象的 描述性统计信息。

​ 在计算机系统里,e+n 代表的是 10 的 n 次方。

2 抽取数据范围(布尔索引)

​ 在pandas库中,有一种筛选数据的方法叫做布尔索引。之前我们有学过使用 df['列索引'] 来提取某一列的信息。在 pandas 库中,我们还可以在 df[] 中通过表达式的形式来提取一定范围的数据。比如我们想选择 mask_data 数据中,单价一列小于等于 200 的数据,就可以设置条件表达式 mask_data['单价'] <= 200。把它加入括号中索引:mask_data[mask_data['单价'] <= 200],就可以选取 mask_data 数据中单价这一列小于等于 200 的数据。

# 筛选单价小于等于 200 的数据
mask_data = mask_data[mask_data['单价'] <= 200]

​ 要注意,布尔索引用的也是中括号嗷。

总结:

2.数据整理
1.日期datetime

① 转换日期数据

​ 在 pandas 库中我们可以使用 pd.to_datetime(arg, format) 来将 DataFrame 对象或者 Series 对象的数据类型转换成 datetime 类型。其中的 arg 参数为我们要转换的数据,它可以是DataFrame 对象或 Series 对象。我们这次要转换的数据为 mask_data 中的’日期’字段,所以对应的 arg 参数就是 mask_data[‘日期’]。format 参数为 datetime 类型的日期格式,比如说这份数据,它是以年-月-日的形式出现的,那么它对应的 format 就是 ‘%Y-%m-%d’(year-month-day的缩写)。

# 转换日期数据,并设置对应的日期格式
date_data = pd.to_datetime(mask_data['日期'], format = '%Y-%m-%d')
# 查看 date_data
date_data

​ 将’日期’字段转换成 datetime 类型后,我们就可以直接从中提取月份信息。

② 提取月份信息

​ 这一步需要用到 Series.dt.month。其中的 Series 指的就是刚才日期数据转换完后的 Series 对象,我将它赋值到了变量 date_data。Series.dt 可以把 datetime 类型的数据转成一种方便我们提取日期或时间的对象。这种对象包含多种属性,其中有 year(年)、month(月)、day(日)等。想要获取这份数据的年、月、日的信息,可以通过 Series.dt.yearSeries.dt.month 以及 Series.dt.day 来获取。

# 提取日期数据中的月份信息
month_data = date_data.dt.month

③ 添加新列

​ 在 pandas 库中,我们可以直接用 df['colname'] = Series 的方式来为原数据添加新的一列。colname 指的是要添加的新列的列名。在这个案例中,colname 就是月份。也就是说直接对新增的这列数据进行赋值,就能为 DataFrame 对象添加新列。因此最后添加’月份’字段到原数据的代码就是:mask_data['月份'] = month_data

# 将月份数据添加到原数据中
mask_data['月份'] = month_data
# 查看原数据
mask_data

④ 总结

2.数据排序

①排序sort_values

sort_values() 方法,可以对指定列进行排序操作。(默认是升序排列)

#根据某列的值进行升序排序
sorted_goods=newgoods_df.sort_values(by='单价')
#如果我们不想对上面的数据集进行排序,只想提取出【单价】列,单独进行排序
newgoods_df['单价'].sort_values()

​ 另外,不单是 DataFrame 对象可以调用 sort_values() 方法,Series 对象也能调用该方法。但要注意的是,使用 DataFrame 对象调用 sort_values() 方法时,可以通过参数 by 来指定某一列。而 Series 对象调用该函数时,函数没有参数 by。原因是 Series 对象不具备列索引(列名)。这一细节点,在书写代码时需要多加留意。

②重置索引

# 重置数据 sorted_goods 的行索引
reset_goods = sorted_goods.reset_index(drop=True)
reset_goods
3.数据四舍五入
# 保留【体重】列的数据到一位小数
humans_df['体重']=round(humans_df['体重'],1)
humans_df

​ 这里有两个是一定要注意的,一个是注意第一行是把df里的某一列重新赋值,而不是对整个df赋值;还要注意round是独立函数,并不需要跟在什么再加个点的后面。

4.函数处理 s.agg(func)

①单个函数处理

​ 方法 agg() 中的参数 func 可以为自定义的函数名,这里在使用时需要注意,传入自定义的函数时,只需要写上函数名,不需要加上括号以及函数的参数。

#定义一个切掉最后一个字符的函数(可用来去掉数值后的单位)
def new_fuc(data):
    number=data[:-1]
    return int(number)

# 使用 agg() 方法对【库存】列的数据进行切片处理
reset_goods['库存'] = reset_goods['库存'].agg(new_func)
reset_goods

​ 其实这个传入的数据类型是要很重视的!!对吧因为这个稍微有那么一点点奇怪至少超出我现有的底层逻辑范围,我暂时就只能构建一下中层逻辑了。

​ 虽说这个函数对传入的data切去了最后一个字符,但我们要认清data的本质,它是某一列中的一个,也就是说,这是对待某一个对象的函数,不是对某一列的函数,所以必然用不到for i in xxx的循环去遍历某一个列呀,因为它在被agg使用的时候就包含了这样的逐个取出了。也就是说,agg这个函数的使用,本身就是对这一列的每一个元素逐个使用了!所以下面这段代码一定是不对的!同时还忘记了非常关键的return!

#错误代码示例
## 定义函数,以替换【数量】列的“-”为 0
#def exchange(data):
#    for i in range(len(data)):
#        if data[i]='-':
#            data[i]=0
#foods_df['数量'].agg(exchange)

#正确代码示范
def exchange(data):
    if data='-':
        data=0
    return data
foods_df['数量'].agg(exchange)

②多个函数处理(传入字典、且只支持部分特定函数)

# 定义字典 dict_2 !! 注意后面不带括号嗷
dict_2 = {
    '单价': 'mean', 
    '库存': 'sum'
}
# 同时计算各食品的平均单价和总库存量
reset_goods.groupby('名称').agg(dict_2)

​ 代码可分为3步进行理解:

1)使用 reset_goods.groupby('名称') 让数据按【名称】列进行分组;

2)定义一个字典 dict_2,字典的键为我们需要操作的列名,如 '单价''库存'。字典的值为我们需要操作的函数名,这里我们需要求平均值函数 mean(),求和函数 sum()。(可使用的函数可参照下表:)同样需要注意的是,操作的函数只需要函数名,不需要函数名后的括号以及函数的参数

3)分组后的数据调用 agg() 方法时,为方法的参数 func 传入字典 dict_2,可以同时得到各食品的平均单价和总库存量。

5.数据类型转换

​ Python 提供了一个 astype() 方法,该方法可以将 Series/DataFrame 对象转换为指定的数据类型。astype() 方法中的参数 dtype,我们可以为其赋上需要的数据类型,如 int、str 等。其中,由于参数 dtype 是 astype() 方法的第一个位置参数,所以我们在使用时无需写为 astype(dtype=int),可以直接写为 astype(int)

6.数据批量替换

​ replace() 方法中的参数 to_replace 可以是一个字典,字典可以将指定的列数据替换为不同的值。

乱入阶段小小结:

5.数据写入

1.csv

​ 使用 df.to_csv() 方法,将清洗好的数据写入 csv 文件中。

# 保存清洗干净的数据
mask_data.to_csv('./工作/mask_data_clean.csv', encoding = 'utf-8')

​ 打开文件后你会发现,最左侧多了一列数据。这个数据是 DataFrame 对象的行索引。pandas 库将 csv 文件读取为 DataFrame对象 时都会自动生成行索引。所以我们写入时也会把行索引单独作为一列写进数据文件中。这里我们可以使用 df.to_csv() 方法的 index 参数来限制 DataFrame 对象的行索引是否写入到 csv 文件。它的默认值为 True,会让你写入的 csv 文件包含一列行索引。如果我们把这个参数设置为 index = False 就可以取消写入行索引,不用将行索引那一列写入 csv 文件。

# 保存清洗干净的数据,并取消写入行索引
mask_data.to_csv('./工作/mask_data_clean.csv', index = False)
2.excel

​ to_excel() 函数可以以 DataFrame 格式将数据写入到 Excel 文件。

​ 因为 Excel 表中默认有行索引,所以如果数据的行索引在工作簿中不具备参考价值,建议不写入到工作簿中。

阶段性小总结:

1.基本的数据处理

2.较为进阶的数据处理

6. 数据分析(分组聚合)

1.单层分组聚合

​ 单层分组聚合操作指的是针对某一个组进行聚合操作。

df.groupby(by)['列索引'].mean()即为按某列索引分组并求出该组的平均值的方法。

​ 注意,那个by的地方,圆括号里带不带方括号都可,结果一样。但是一定要理解原理,这里的方括号可行只是因为可以看作一个只有一个内容的列表吧,因为有多层分组的存在才导致了它可行,但你可以看到,它绝对不是作为col那样的含义,因为在多层里再套方括号,它就不行了。

grade_df2 =grade_df.groupby(['性别'])['成绩'].median()
grade_df2 = grade_df.groupby('性别')['成绩'].median()

​ 求平均值只是聚合操作的一种,聚合操作还有很多,几个常用的聚合操作方法如下:

2. 多层分组聚合

​ 唯一的不同点在于索引的层数上。层数的不同体现在参数 by 的赋值方式上。在单层分组聚合操作的时候,只需要将一个列索引传给参数 by 就可以了。而多层分组聚合操作需要传多个列索引给参数 by。

​ 需要注意的是,这些列索引在传进参数 by 之前,需要先被放进一个“容器”里,这个容器可以是列表。

# 获取不同性别的学生处于不同班级时的平均分
grade_df4 = grade_df.groupby(['性别', '班级'])['成绩'].mean()
# 查看grade_df4
grade_df4

​ 注意,里面可不能再套方括号啦。

如果再多一层:

​ 虽然通过多层分组聚合的方法,可以求出不同组别下的学生考试成绩的情况,但是返回的结果看起来却有些冗杂,所有的组别信息都扎堆出现在了一个 Series 对象的索引中。

s.unstack() 函数就是解决这个问题的常见方法。它可以将一个多层分组聚合后的 Series 对象转变成 DataFrame 对象。

# 对grade_df5使用unstack函数
grade_df5.unstack()

s.unstack() 这个方法是针对多层分组聚合后的 Series 对象来使用的。作用就是将其索引的最后一列转变成 DataFrame 对象的列索引,而剩下的索引则转变成 DataFrame 对象的行索引。

# 获取上图所示的表格
grade_df6 = grade_df.groupby(['班级', '眼镜', '性别'])['成绩'].mean().unstack()
# 查看grade_df6
grade_df6

7.数据可视化

1.导入中文字体
# 导入matplotlib库的pyplot模块
from matplotlib import pyplot as plt
# 设置中文字体
plt.rcParams['font.family'] = ['Source Han Sans CN']

plt.rcParams['font.family'] 可以获取 matplotlib 库中的字体,'Source Han Sans CN' 指的是我们课程系统自带的中文字体。plt.rcParams['font.family'] = ['Source Han Sans CN'] 意味着我们将课程中自带的中文字体添加到了 matplotlib 的字体库中。

​ 而不同系统下自带的中文字体是不一样的,以下为Windows 和 Mac OS 系统下的常用中文字体。

2.单条折线图
series.plot(kind='line', figsize=(7, 7), title='此人月考成绩')

​ 绘制单条折线图的代码是:s.plot()。同样的,这个方法有很多可以传入的参数,主要的是以下三个参数:

3.多条折线图
# 创建三位学生成绩的df
students_grade = pd.DataFrame({'李健': [80, 85, 89, 91, 88, 95],
                               '王聪': [95, 92, 90, 85, 75, 80],
                               '过凡': [90, 91, 92, 91, 90, 91]
                              }, index=['2月', '3月', '4月', '5月', '6月', '7月'])
# 绘制多条折线图
students_grade.plot(kind='line', figsize=(7, 7), title='月考成绩')

多条折线图的绘制方法是:df.plot()。它和单条折线图相比,最大的区别就在于:多条折线图是针对一个 DataFrame 对象来绘制的,而单条折线图是针对一个 Series 对象来绘制的.df.plot() 默认会将每一列数据用 s.plot() 绘制成单条折线图,然后合并到同一张图上。而刚才提到的三个参数:kind、figsize、title 以及补充给你的几个参数,df.plot() 都是可以沿用的,效果也相同。

4.饼图
# 导入数据
my_data = pd.read_csv('./工作/clean_data.csv', encoding='utf-8')
# 查看my_data
my_data
# 获取行业的频率分布
profession = my_data['行业'].value_counts()/my_data['行业'].value_counts().sum()
# 绘制行业频率分布的饼图
profession.plot(kind='pie', autopct='%.2f%%', figsize=(7, 7), title='行业频率分布图', label='')
5.条形图
# 获取岗位的频率分布
position = my_data['岗位'].value_counts()/my_data['岗位'].value_counts().sum()
# 绘制岗位的频率分布条形图
position.plot(kind='bar', figsize=(13, 6), title='岗位频率分布条形图')

(三)matplotlib

0.数据可视化工具

​ 首先介绍一下数据可视化的主流工具:

​ Tableau 和 PowerBI 均为数据可视化工具的头部选手,它们的功能比较接近,主要是制作数据看板。其中 Tableau 偏商业分析,可视化偏标准化,需要付费。PowerBI 适合所有类型的用户,可视化更具定制化,但是自定义和共享仪表板需要付费。而 Python 作为一种编程语言,通过调用第三方可视化库作图,覆盖的图表种类很多,定制化非常强,并且免费。再加上 Python 基本涵盖了数据分析的整个流程:从数据处理到数据分析,再到数据展现。可以说是行云流水,一气呵成。

​ 在 Python 中有几个常用的第三方可视化库:pandas、matplotlib 以及 seaborn。

​ 更倾向于推荐 matplotlib 库,主要原因是:

1)matplotlib 库相较于 pandas 绘图以及 seaborn 绘图工具更为底层,因此,matplotlib库中的绘图函数、参数相对更多,我们可以根据自己的风格自由选择。

2)从应用范围上来看,在 python 数据分析领域,matplotlib 库是与 numpy 库、pandas 库并驾齐驱的三库之一,被称作 python 数据分析的“三剑客”,由此可见其应用范围之广。

1.导入库
from matplotlib import pyplot as plt
2.画布的生成与保存

​ 生成画布用到的是 pyplot 模块下的 figure 函数,即 plt.figure()。其中的参数 figsize 可以控制画布的长和宽,一般用元组的形式进行赋值。

# 生成画布,并设置画布的大小
plt.figure(figsize=(6, 6))

​ 输出结果<Figure size 432x432 with 0 Axes>表示生成了一个空白的画布对象。画布大小为 6 英寸x6 英寸,默认分辨率为 72 像素/英寸,所以像素为 432x432。

​ 如果要将 jupyter 沙盒中生成的图形保存到本地,可以用 plt.savefig()对画布进行保存,这里只需要设置它的路径参数。

# 前面已经作完图了哈
# 保存画布,并设置保存路径
plt.savefig('./工作/各月总订单量趋势图.png')
3.设置x/y坐标值
1.直接用列表创建series对象

​ x 是指坐标点的横坐标(简称 x 坐标值),y 是指坐标点的纵坐标(简称 y 坐标值),它们均为可迭代对象,你可以理解为有序的元素序列,比如 x = (x1, x2, x3, ……, xn)y = (y1, y2, y3, ……, yn)

​ 这里以 Series 对象为例设置 x/y 坐标值。

# 设置 x/y 坐标值
x = pd.Series(['第一季度', '第二季度', '第三季度', '第四季度'])
y = pd.Series([160, 200, 180, 155])
2.读取已有的series对象
# 设置 x 坐标值
x=series.index
# 设置 y 坐标值
y=series.values
4.绘制图表
1.折线图
# 生成画布,并设置画布的大小
plt.figure(figsize=(6, 6))
# 设置 x/y 坐标值
x = pd.Series(['第一季度', '第二季度', '第三季度', '第四季度'])
y = pd.Series([59, 70, 68, 56])

# 绘制折线图,并调整线条颜色为湖蓝色
plt.plot(x, y, color='dodgerblue')

相关常用参数:

matplotlib相关颜色:

​ 参数 marker 可以设置数据标记点的形状,有点、圆、加号等,常见为字符串类型,有以下这些:

2.柱状图

​ 和折线图一样,绘制柱状图也需要设置 x/y 坐标值,不同的是柱状图绘图函数 plt.bar()的参数是 xheightheight 表示柱子的高,对应 y 坐标值。

# 生成画布,并设置画布的大小
plt.figure(figsize=(6, 6))
# 设置 x/y 坐标值
x = pd.Series(['第一季度', '第二季度', '第三季度', '第四季度'])
y = pd.Series([59, 70, 68, 56])

# 绘制柱状图,并调整颜色为深橘色,透明度为 60%
plt.bar(x, height=y, color='darkorange', alpha=0.6)
3.饼图

​ 饼图和折线图、柱状图在图像特征方面差异蛮大的,它只用到了 x 表示饼图中各个扇形面积的大小。

# 生成画布,并设置画布的大小
plt.figure(figsize=(6,6))
# 设置扇形面积值(x值)
x=pd.Series([59,70,68,56])
# 设置百分比小数的位数:保留百分比小数点后两位
autopct='%.2f%%'
# 设置百分比字体大小和颜色
textprops={'fontsize':12,'color':'black'}
# 设置饼图的“爆炸效果”:让扇形远离圆心
explode=[0.1,0,0,0]
# 设置不同扇形的颜色
colors=['cornflowerblue', 'salmon', 'yellowgreen', 'orange']
# 绘制饼图
plt.pie(x,autopct=autopct,textpros=textpros,explode=explode,colors=colors)

5.设置图表标题

​ 图表标题默认放在图表顶部,向读者传递图表的名称。设置图表标题用的是 plt.title()函数,包含两个参数:第一个参数 label 设置图表的标题名,常见为字符串类型;第二个参数 fontsize 设置标题的字体大小,需要向它传入一个代表字体大小的数值。

# 生成画布,并设置画布的大小
plt.figure(figsize=(6, 6))
# 绘制柱状图
plt.bar(x, height=y, color='darkorange', alpha=0.6)

# 设置图表标题名及字体大小
plt.title('2020年各季度研发费用分布图', fontsize=20)

​ 稍稍拓展一下,如果想设置更丰富的字体样式,可以用 fontdict 参数代替 fontsize。fontdict 是一个包含许多参数的字典,以思源黑体字体为例,详见以下表格👇。

# 生成画布,并设置画布的大小
plt.figure(figsize=(6, 6))
# 绘制柱状图
plt.bar(x, height=y, color='darkorange', alpha=0.6)

# 设置图表标题名及字体大小
plt.title('2020年各季度研发费用分布图', fontdict={
        'family': 'Source Han Sans CN', 'color': 'blue', 'weight': 'bold', 'size': 16})
6.设置坐标轴

​ 主要考虑坐标轴刻度和坐标轴标题:

1.坐标轴刻度

​ 设置 x、y 轴刻度的函数分别是 plt.xticks()plt.yticks()。调用这两个函数,就可以根据 x、y 坐标值自动生成 x、y 轴刻度字体大小。

# 生成画布,并设置画布的大小
plt.figure(figsize=(6, 6))
# 设置 x/y 坐标值
x = pd.Series(['第一季度', '第二季度', '第三季度', '第四季度'])
y = pd.Series([59, 70, 68, 56])
# 绘制柱状图
plt.bar(x, height=y, color='darkorange', alpha=0.6)

# 设置坐标轴的刻度字体大小
plt.xticks(fontsize=12)
plt.yticks(fontsize=12)

​ 当然,坐标轴刻度是可以自定义的,比如调整刻度的间隔、标签名称等。不过在此先不作过多赘述。

2.坐标轴标题
# 设置坐标轴的标题名及字体大小
plt.xlabel('季度', fontsize=15)
plt.ylabel('研发费用(百万元)', fontsize=15)

​ 拓展一下,在上述函数中还可以添加参数 hava,设置标题文本的对齐方式:与水平对齐方式有关的参数 ha,可选’left’、‘right’、'center’等,以及与垂直对齐方式有关的参数 va,可选 ‘center’、 ‘top’、 ‘bottom’、 'baseline’等。

7.设置图例

​ 设置图例用到 plt.legend()函数,其中的参数labels表示图例名称,对应图中的多个条件,通常传入可迭代对象,比如列表、Series 对象等。

# 设置图例为'研发费用变化'、'研发费用分布'
plt.legend(['研发费用变化','研发费用分布'])
8.设置数据标签

​ 数据标签是指坐标点上方显示的标签,用于呈现每个坐标点的数据信息。添加数据标签用到 plt.text()函数,它有几个参数,分别是 x、y、s、ha、va 以及 fontsize。

​ 调用 plt.text()函数可以在图表的指定位置添加文本,但是每次只能添加一个。这里需要重点强调 x、y 的用法,xy 表示所添加文本的位置,受到函数本身的属性限制,常见为数值或者字符串类型,这与绘图函数中 x、y 可以传入可迭代对象有很大的差异。

​ 更为简洁的代码可以借助 zip()函数实现。可以将 zip()函数理解为拉链,x=(x1, x2, x3,……, xn)为拉链的左边,y=(y1, y2, y3,……, yn)为拉链的右边,经过 zip()函数,就合并为(x1, y1), (x2, y2), (x3, y3),……,(xn, yn)

zip()函数可以从 x、y 元素序列中依次取出元素,按照顺序进行一对一匹配。

# 设置数据标签
for a, b in zip(x, y):
    plt.text(a, b, b, ha='center', va='bottom', fontsize=12)
9.总结

​ 其实有的时候要注意的点是,每个要传进去的参数是什么格式,比如数字、字符串、列表、字典,这些一定要搞清楚了。我觉得这一切都和维度有关,比如列表,它是在同一个维度进行纵向延申的,只要封在了一个列表里,它就是一个类型的东西,你加进去很多个都是同类型的具体对应不同而已;但是字典就用在多个维度,比如每一个东西对应了一个什么或几个什么。所以我感觉,很多时候,用列表是为了强调它们的并列性,防止被认为是多个维度的东西;而用字典传入是因为确实是同一事物的多个维度,所以只能用字典传入。

阶段小结:

(四)关联分析与Apriori算法

1.关联分析

关联分析,是一门分析技术,用于发现大量数据中,各组数据之间的联系

​ 交易中的不同物品可称为一个。所以,这4条交易记录,一共含有4个项(商品类目):“薯条”,“可乐”,“奶茶”,“汉堡”。0个或多个项的集合,可称为一个项集,一般用{X}的形式表示项集,k 个项组成的项集,叫 k 项集。

1.支持度

​ **支持度(Support)**可以表示项集在事务中出现的概率(频率),你也可以理解成顾客对某一个项集的“支持程度”。

{X}支持度 = {X}在事务中出现的次数 / 事务总数

​ 需要人为地设定一个支持度,名为最小支持度,用于筛掉那些不符合需求的项集。被留下来的项集(≥ 最小支持度),被称为频繁项集

​ 有了频繁项集,就可以生产关联规则了。关联分析是探索数据之间联系的技术,而数据之间的联系,我们用关联规则来表示,表达式为:{X}→{Y}(X 和 Y 之间不存在相同项)。规则有顺序之分,为了方便描述,我们把规则前面的项集叫前件,把规则后面的项集叫后件。但是,怎么知道这种联系是否可靠?如果可靠,到底有多可靠?哪个更可靠?

#####2.置信度

置信度(Confidence)可用于衡量关联规则的可靠程度,表示在前件出现的情况下,后件出现的概率。一般来说,概率越高,规则的可靠性越强。

​ 关联规则{X}→{Y}置信度 = {X,Y}支持度 / {X}支持度

​ 同为关联规则,可靠程度有“强”有“弱”。在实际业务中,也需要人为地设定置信度,名为最小置信度,用于筛掉一些不符合需求的关联规则。被留下来的关联规则( ≥ 最小置信度),叫做强关联规则

​ 众所周知,在自然界,不同物种之间,不仅有“互惠互利”的合作关系,也有“势不两立”的竞争关系。关联规则也一样,既有促进关系,也有抑制关系。因而,还需引入**提升度(Lift)**对它们进行判断。

3.提升度

{X}→{Y}提升度 = {X}→{Y}置信度 / {Y}支持度,意思是评估 X 的出现,对 Y 出现的影响有多大。

总结:

2.Apriori算法

Apriori是用于挖掘出数据背后的关联规则的一种算法,以下简写为 Apr 算法,它的流程可分为两步(简单了解它的运作原理即可,无需深究):

1.确定最小支持度和最小置信度

​ 判断它们是否“合适”的感觉很微妙,没有特定的标准答案

​ 其实总结起来就一个字:。一开始设定的最小支持度和置信度和理想状态有一定偏差也没关系,后续再慢慢调整

#####2.找出频繁项集和强关联规则

######1.频繁项集

​ 这一步,Apr 的算法主要依赖两个性质:

1)一个项集如若是频繁的,那它的非空子集也一定是频繁的。假如购买{薯条,奶茶}的概率都很高,那购买{薯条}或{奶茶}的概率肯定也很高;

2)一个项集如若是非频繁的,那包含该项集的项集也一定是非频繁的。假设购买{薯条}的次数少,那购买{薯条,奶茶}的次数肯定也少。

​ 在 Apr 算法出现之前,要找出所有的频繁项集,就得先枚举所有的项集,计算它们的支持度,然后和最小支持度(0.2)进行对比,筛选出频繁项集。

​ 但 Apr 算法不一样,它对错误的“道路”进行了提前预判。一旦找到某个不满足条件的“非频繁项集”,包含该集合的其他项集不需要计算,更不用对比,通通绕开

​ 凭借新生成的频繁项集,就可以开始“制造”关联规则了。

2.关联规则

​ 值得注意的是,因为频繁 1 项集只有一个项,无法构成具有指导意义的关联关系(≥ 2 项),可直接忽略。

​ Apr 算法会先产生一系列后件项数1的关联规则,与最小置信度进行比较,得到一部分强关联规则。然后,频繁项集继续生成后件项数2的关联规则,再对它们的置信度进行比较,又收获一批强关联规则。当无法从剩下的频繁项集中生成新的关联规则时,该过程就结束了。

​ 关联规则的支持度,和构建规则的频繁项集的支持度一致。

​ 若是这些强关联规则正好是你想要的,那就进一步计算它们的提升度。相反,若是你对筛选出来的强关联规则不满意,那我们就得重新调整最小支持度和最小置信度,再计算一次。

​ 尽管 Apr 算法已经对原始的关联分析做了优化,但手动计算依然繁琐,特别是当我想要调整最小支持度或者最小置信度的时候。如果能把 Apr 算法的计算流程抽象成函数,将最小支持度、最小置信度和最小提升度设置成参数,通过调整参数来查看关联规则,想怎么调就怎么调就好了。实际上,Python 早已可以实现。

3. Python 调用 apriori 函数

​ 提供这一便利的是apyori模块下的apriori函数, apyori模块属于 Python 的第三方模块。

from apyori import apriori
orders = [['薯条', '可乐'], ['薯条', '可乐', '奶茶'], ['汉堡', '薯条', '可乐'], ['汉堡', '可乐']]
results=apriori(orders,min_support=0.2,min_confidence=0.7)
results

​ 这段代码返回的是一串<generator object apriori at 0x7fa21646ec50><generator>是一个生成器对象,它是一种用于节省空间的运算机制,可以通过循环遍历的形式对它里面的数据进行访问:

for result in results:
    print(result)

​ 得到的是一堆略显复杂,但实际具有很强结构性的东西:(每个都不过是自定义元组内嵌套列表又在内嵌套了一层自定义元组罢辽,逐层提取然后把各个有意义的数据转化成所需的数据结构就好)

RelationRecord(items=frozenset({'可乐'}), support=1.0, ordered_statistics=[OrderedStatistic(items_base=frozenset(), items_add=frozenset({'可乐'}), confidence=1.0, lift=1.0)])
RelationRecord(items=frozenset({'薯条'}), support=0.75, ordered_statistics=[OrderedStatistic(items_base=frozenset(), items_add=frozenset({'薯条'}), confidence=0.75, lift=1.0)])
RelationRecord(items=frozenset({'奶茶', '可乐'}), support=0.25, ordered_statistics=[OrderedStatistic(items_base=frozenset({'奶茶'}), items_add=frozenset({'可乐'}), confidence=1.0, lift=1.0)])
RelationRecord(items=frozenset({'汉堡', '可乐'}), support=0.5, ordered_statistics=[OrderedStatistic(items_base=frozenset({'汉堡'}), items_add=frozenset({'可乐'}), confidence=1.0, lift=1.0)])
RelationRecord(items=frozenset({'薯条', '可乐'}), support=0.75, ordered_statistics=[OrderedStatistic(items_base=frozenset(), items_add=frozenset({'薯条', '可乐'}), confidence=0.75, lift=1.0), OrderedStatistic(items_base=frozenset({'可乐'}), items_add=frozenset({'薯条'}), confidence=0.75, lift=1.0), OrderedStatistic(items_base=frozenset({'薯条'}), items_add=frozenset({'可乐'}), confidence=1.0, lift=1.0)])
RelationRecord(items=frozenset({'薯条', '奶茶'}), support=0.25, ordered_statistics=[OrderedStatistic(items_base=frozenset({'奶茶'}), items_add=frozenset({'薯条'}), confidence=1.0, lift=1.3333333333333333)])
RelationRecord(items=frozenset({'薯条', '奶茶', '可乐'}), support=0.25, ordered_statistics=[OrderedStatistic(items_base=frozenset({'奶茶'}), items_add=frozenset({'薯条', '可乐'}), confidence=1.0, lift=1.3333333333333333), OrderedStatistic(items_base=frozenset({'奶茶', '可乐'}), items_add=frozenset({'薯条'}), confidence=1.0, lift=1.3333333333333333), OrderedStatistic(items_base=frozenset({'薯条', '奶茶'}), items_add=frozenset({'可乐'}), confidence=1.0, lift=1.0)])
RelationRecord(items=frozenset({'薯条', '汉堡', '可乐'}), support=0.25, ordered_statistics=[OrderedStatistic(items_base=frozenset({'薯条', '汉堡'}), items_add=frozenset({'可乐'}), confidence=1.0, lift=1.0)])

​ 先来简单了解一下几个陌生的数据类型:

  • RelationRecord(关系记录)和 OrderedStatistic(有序的统计数据)是模块作者自定义的命名元组(namedtuple),可以理解成一种通过名称访问的元组。

  • frozenset是一种不可变的集合,使用list()函数,可以将它转换成我们熟悉的列表形态。

    然后再来了解一下数据的结构:

  • 一条“关系记录”(RelationRecord),包含了频繁项集的基本信息:频繁项集items,支持度support和统计列表ordered_statistics

  • 一条“统计列表”(ordered_statistics),包含了该频繁项集下构建的所有强关联规则OrderedStatistic

  • 一条“强关联规则”(OrderedStatistic),包含了该规则的基本信息:前件items_base,后件items_add,置信度confidence,提升度lift

    ​ 虽然这样一梳理,数据清晰了不少,但这样的展现形式还是太“反人类”了,需要进一步抽取关键信息。抽取的重点,是由前件和后件组成的强关联规则,以及它们的支持度置信度提升度。若找到前件为空集的强关联规则,那么,这条规则其实没有什么现实意义,可直接删除。

results=apriori(orders,min_support=0.2,min_confidence=0.7)
for result in results:
    support=round(result.support,3)
    for rule in result.ordered_statistics:
        head_set=list(rule.item_base)
        tail_set=list(rule.item_add)
        if head_set=[]:
            continue
        related_catogry=str(head_set)+'→'+str(tail_set)
        confidence=round(rule.confidence,3)
        lift=round(rule.lift,3)
        print(related_catogory,support,confidence,lift)

(五)RFM模型

1.模型简介

1.原理

1)R(Recency):最近一次消费时间间隔,指用户最近一次消费时间距离现在的时间间隔,上一次消费时间离现在越近,再次消费的几率越大。即 R 值越小,用户的活跃度越大,用户的价值就越高;

2)F(Frequency):消费频率,指用户一段时间内消费了多少次,购买频率越高,说明用户对品牌产生一定的信任和情感维系。即 F 值越大,用户的忠诚度就越大,用户的价值就越高;

3)M(Monetary):消费金额,指用户一段时间内的消费金额,消费金额越高,说明用户对产品的购买力越大。即 M 值越大,用户的购买力就越大,用户的价值就越高。

​ 这三项数据作为衡量客户价值和客户创利能力的重要工具和手段,也是 RFM 模型的三个重要指标。

2.作用

​ RFM 模型多用于精细化运营服务。

​ 如果根据 RFM 模型将用户细分为 8 类,还能进一步对不同价值的用户使用不同的运营策略,获取并保留关键性用户,针对价值高的客户制定促销策略。

2.RFM模型构建流程

1.计算R、F、M的值

​ 得到 R、F、M 这 3 个指标,一般需要的信息有:用户名称/用户 ID、消费记录(如消费时间、消费金额)。

#####2.根据 RFM 的阈值,对用户进行分类

​ 获得阈值,可以对 RFM 各值采取分区域评分,再计算各值平均数的方式,该方式会分为三个步骤:

1)给 R、F、M 各值按价值划分打分区间(如何定义打分的范围,需要结合具体的业务来调整)

2)计算价值的平均值

3)用户分类(将两个用户的 RFM 值与各值的平均值进行对比。)

​ 如果一行里的 R 值打分大于平均值,就标记该行的 R 值打分为“高”,反之标记为“低”。F、M 值亦是同理。

​ 然后,再将标记好的 RFM 高低值与用户分类规则表进行对比,可以得出用户属于哪种类别。

(六)NumPy

1.numpy简介

​ NumPy 是一个开源的 Python 科学计算基础库。

1)在性能方面:NumPy 的计算操作由预编译好的 C 代码快速执行。所以比之 Python,它的运算速度常常更快。

2)在存储方面:NumPy 同样也设计了一个更好的存储结构来提高计算效率。

​ 第一板块,可以说是最基础的板块:ndarray 数组。

​ 第二个板块:ufunc 通用函数。就像一套能处理数组的工具,它能对 NumPy 数组中的元素逐个进行操作。和 Python 内置的 math 模块类似,它主要用于实现一些较为基础的数组运算,例如求和、求平方根等。

​ 第三个板块是用于科学计算的子模块包。这些子模块包内包括随机数包、线性代数包,矩阵包等内容。这些子模块包,就像各种能处理数组的工具包。在这些工具包内还有各式各样的工具来帮助你完成复杂的科学计算。

导入numpy库:

import numpy as np

2.ndarray数组

1.n维数据

① 一维

​ 一维数据一般由线性方式组织的数据构成。

② 二维

​ 二维数据是由多个一维数据构成的数据,它是一维数据的组合。

​ 平时常见的表格同样也是由多行一维数据堆叠而成的。它也是一种典型的二维数据。

③三维

​ 三维数据是由一维数据或二维数据在一个新的维度上扩展而成的。

​ 同为二维数据的表格在时间维度做上拓展后,也可以变成三维数据。

2.创建 ndarray
 np.array(列表/元组)

传入一维列表

# 创建一维的列表 list_1
list_1 = [1, 2, 3]

# 创建数组 array_1
array_1 = np.array(list_1)
# 查看数组 array_1
array_1

# array([1, 2, 3])

传入二维列表

# 创建二维的列表 list_2
list_2 = [[0, 0, 0],
          [10, 10, 10],
          [20, 20, 20],
          [30, 30, 30]]

# 创建数组 array_2
array_2 = np.array(list_2)
# 查看数组 array_2
array_2

#array([[ 0,  0,  0],
#       [10, 10, 10],
#       [20, 20, 20],
#       [30, 30, 30]])

传入三维列表

# 创建三维的列表 list_3
list_3 = [[[0, 0, 0],
           [10, 10, 10],
           [20, 20, 20],
           [30, 30, 30]],
          [[0, 0, 0],
           [10, 10, 10],
           [20, 20, 20],
           [30, 30, 30]],
          [[0, 0, 0],
           [10, 10, 10],
           [20, 20, 20],
           [30, 30, 30]]]

# 创建数组 array_3
array_3 = np.array(list_3)
# 查看数组 array_3
array_3

#array([[[ 0,  0,  0],
#        [10, 10, 10],
#        [20, 20, 20],
#        [30, 30, 30]],

#       [[ 0,  0,  0],
#        [10, 10, 10],
#        [20, 20, 20],
#        [30, 30, 30]],

#       [[ 0,  0,  0],
#        [10, 10, 10],
#        [20, 20, 20],
#        [30, 30, 30]]])

​ 除了 array() 方法,NumPy 还提供了其他办法来创建数组,有需要时,可以再补充学习。以下表格内提到的方法各有优劣。而在使用时,我们可以根据实际情况来选择对应的方法创建数组。

​ 补充一点,不同于 Python ,ndarray 数组要求所有的元素类型相同。所以,通过 array() 方法创建数组时,传入的数据参数需要统一其内部元素的数据类型。

3.ndarray的特征
1.维度(ndim.shape.size)

①相关概念

​ 先明确两个概念:轴(axis)秩(rank)

1)轴(axis)简单来说就是维度。在实际使用时,我们会用从 0 开始的数字表示不同的维度,像:axis = 0、axis = 1、axis = 2 等。

2)秩(rank)比较好理解,就是指维度的数量。

数组每次新增的轴,也就是最外围的维度,都为 0。

②查看属性

​ 3 个可用于查看数组维度信息的属性,它们分别是:shape、ndim、size。

1)shape 属性表示数组的形状,即各个维度上的数据长度。当你调用它时,会返回一个元组作为结果。

# 查看数组 array_2
array_2

#array([[ 0,  0,  0],
#       [10, 10, 10],
#       [20, 20, 20],
#       [30, 30, 30]])

# 调用属性 shape,查看数组形状
array_2.shape

#(4, 3)

​ 一定要注意,这个排列顺序是从axis=0开始的,但是axis=0对应的始终是最新加进去的那一个维度(上图其实表达的已经很直观了)。如果从它形式上的格式来理解,就是从最外面一层中括号开始剥,最外面第一层剥开有几个并列的,它在axis=0上的长度就是几,再往里剥,axis逐渐变大。

2)ndim 属性能表示轴/维度的数量,调用 ndim 属性后,代码会以整数形式返回数组的维度数量。

# 查看数组 array_2
array_2

#array([[ 0,  0,  0],
#       [10, 10, 10],
#       [20, 20, 20],
#       [30, 30, 30]])

# 调用属性 ndim,查看数组的维度数量
array_2.ndim

#2
  1. size 属性表示数组元素的个数,例如一个 n 行 m 列的二维数组的元素个数就是 n * m。
# 调用属性 size,查看数组的元素个数
array_2.size

#12

​ 其实可以说,size属性对应的那个数字,就是shape属性里的几个数字相乘。

小总结:

2.索引

​ 同 Python 的列表类似,ndarray 数组也有能对元素进行索引操作的特点。索引操作用于获取数组中特定位置的元素。

①普通索引

​ 一维数组中,单个元素的索引会与列表的索引很相似。数组中的元素会有从 0 开始的数字作为其索引下标。

array_1[索引值]

​ 多维数组中的单个元素的索引,其实也和列表的嵌套索引很类似。在每条轴的方向上,我们分别取一个索引值,然后用逗号分割即可。

array_1[索引值0,索引值1]
array_1[索引值0,索引值1,索引值2]

​ 不过还是要注意,这个顺序还是按照axis从0到n的顺序从左到右的。也就是说,依然是比低一阶所新增的那个维度是0,或者说是最外面的那一层括号是axis=0。

②布尔索引

# 打印布尔数组方便查看
print(array_2 < 20)

#[[ True  True  True]
# [ True  True  True]
# [False False False]
# [False False False]]


# 用布尔数组索引数组 array_2 中的特定元素
array_2[array_2 < 20]

#array([ 0,  0,  0, 10, 10, 10])

​ 要注意嗷,这里使用布尔索引就和位置毫无关系啦!它只取决于各位置上元素是否满足条件,而与位置坐标无关,赋值True/False以后,其值为True的会被提取出来。(其实跟前面那个pd里对df的操作挺相似的。)

③切片

​ 切片是获取数组元素子集的过程。它和列表的切片有点类似,简单来说,就是取出两个索引中间夹着的元素。

​ 一维数组:

ndarray[起点索引值: 终点索引值(取值时不包括): 步长]

​ n维数组就是中间用逗号隔开了,还是要注意谁是axis=0就好。

#利用索引配合切片来取该数组中所有第二行的元素
#array([[[0.2, 0.2, 0.2],
#        [0.3, 0.3, 0.3],
#        [0.4, 0.4, 0.4],
#        [0.5, 0.5, 0.5]],

#       [[0.2, 0.2, 0.2],
#        [0.3, 0.3, 0.3],
#        [0.4, 0.4, 0.4],
#        [0.5, 0.5, 0.5]],

#       [[0.2, 0.2, 0.2],
#        [0.3, 0.3, 0.3],
#        [0.4, 0.4, 0.4],
#        [0.5, 0.5, 0.5]]])

# 切片取数组 array_b 中的目标元素
array_b[:, 1, :]

方法:只要想象那个空间图就好了。

3.元素的数据类型

在 ndarray 数组中,常见的元素数据类型有:

​ 不同于 Python,这里整数、浮点数等都被区分为了各种位的长度。位的长度这个概念涉及一些计算机底层的理论,我们这里先暂时不去理解它。只需要知道之所以 NumPy 要如此精细管理 ndarray 的元素类型,是因为这样有助于它合理使用存储空间并优化性能。

4.向量运算

1)两个数组 a 和 b 形状相同,即满足a.shape == b.shape时。此时,两个数组在加减乘除后,它们内部对应位置的元素会一一对应做加减乘除。

2)数组与一个形状不一样的数据进行运算时。但在这种情况下进行运算,不是随便两个形状不一样的数组都能进行运算的。

​ 关于这一点,NumPy 有一套计算规则叫“广播”(Broadcast)。把官方说明翻译过来就是:两个形状不同数组间运算时,它们对应维度的长度必须相同,或者其中一方为 1(这是和常规计算好像不太一样的!!),才可以运算成功,否则就会报错。

4.总结

(七)Seaborn

1.seaborn概述

1.介绍

1)Relational(关系)可以用来展示定量数据之间关系的趋势,如离散程度或者数据随时间变化的情况。

2)Distribution(分布)可以用来展示定量数据的分布信息,比如频数分布情况,数据的形态等。
3)Categorical(分类)可以用来展示定性数据之间的分布或关系之类的信息,比如不同类别数据的大小,分布情况等。

​ 其他的,如:Regression(回归) 可用于画出线性回归图形,Matrix(矩阵)可以画热力图和聚类图,Multiple(复合)可以用多个子图或者二元图实现不同条件之间的关系、频数分布等功能。

2.导入&中文字体调整
import seaborn as sns
import matplotlib.pyplot as plt
# 设置中文字体(这一段在前面已经比较具体的整理过了)
plt.rcParams['font.sans-serif'] = ['Source Han Sans CN']

2.Relational 中的常见图形

1.散点图
1.介绍

​ 散点图可以比较直观地观察变量 x 与变量 y 之间的相互影响程度。对于那些变量之间存在密切关系,但是这些关系又不像数学或物理公式那样能够精确表达的,散点图是一种很好的图形工具。

​ 在散点图中,如果变量之间不存在线性关系,则表现为随机分布的离散的点;如果变量间存在某种线性关系,那么大部分的数据点就会相对密集并以某种形态呈现。

​ 在观察散点图的时候,还有一个概念需要我们理解——离群点。当变量间存在某种线性关系时,大部分的数据点会表现出相对密集的形态,但总有几个不合群的数据点离集群较远,我们可以称这些点为离群点或异常点。

2.语法

​ 官方文档推荐我们使用 seaborn.relplot() 这个函数来画散点图,记忆的话很容易,relplot 其实是 Relational Plot(关系图形)的缩写,我们要画的图是散点图,散点图属于关系图形,脑中建立起这样的联系,就不用去死记硬背函数名了。seaborn.relplot() 的语法和常用参数如下图所示:

​ 其中,参数 xydata 都是必选参数,x 和 y 一般为连续数据,可以理解为上面说的变量 x 和变量 y;data 指的是需要展示的数据集,推荐先把数据通过 pandas 读取成 DataFrame 类型后再传给参数 data。

​ 可选参数hue,通过图中的不同颜色表示不同含义,可以让我们在图中添加一个维度。

​ 可选参数 size,它会根据传入数据的大小在散点图中生成大小不同的点。比如,我们把 ‘身高’ 传给这个参数,那么在生成的图形中,身高越大,点也就越大,反之亦然。

​ 可选参数style,它可以在某一维度上,用点的不同形状区分数据。

​ 还有一个可选参数 kind,被我隐藏掉了,其值默认为 'scatter'(散点图)。在画散点图时,可以不用写这个参数。

import pandas as pd
student_info=pd.read_csv('./source-data/学生信息.csv')
sns.relplot(x='身高',y='体重',data=student_info,hue='性别',size='体重',style='班级')

​ 小tips:根据科学研究,人的眼睛对颜色比对形状更敏感,因此,在做可视化时,我们可以优先考虑使用颜色来区分不同的数据。

2.折线图

​ 折线图也可以反映数据间的关系,只不过它一般使用时间维度作为 X 轴,数值维度作为 Y 轴。当我们想要了解某一维度在时间上的规律或者趋势时,用折线图是个比较不错的选择。relplot() 函数也是可以画出折线图的,基本的折线图只需要在必选参数的基础上,写上我们刚才隐藏的参数 kind='line' 即可。

population = pd.read_csv('./source-data/人口_sim.csv')
sns.relplot(x='年份', y='年末总人口(万人)', data=population, kind='line')

3.Distribution中的常见图形

​ 本节将着重于直方图的相关知识和语法,至于其他涉及较复杂统计知识的图形,暂时先不讲。

1.直方图概念

​ 直方图用于展示定量数据的分布情况,我们可以通过直方图来观察数据分布的大体形状,比如:数据分布是否对称,是右偏还是左偏等。

​ 如果数据分布大体对称的话,图形会呈现出中间高,两边低,左右近似对称的特点,就像上图中间所示形状一样。

​ 如果数据的众数 < 中位数 < 平均数,那么分布大体就会呈现出右偏分布的情况,形状上类似上图左侧部分。

​ 如果数据的众数 > 中位数 > 平均数,那么分布大体就会呈现出左偏分布的情况,形状上类似上图右侧部分。

2.直方图语法

​ 必选参数 xdata,与之前学过的一样。x 一般为连续数据,data 指的是我们要展示的数据集。值得注意的是,在直方图的函数中,少了必选参数 y,这是直方图定义造成的结果。

titanic_info = pd.read_csv('./source-data/泰坦尼克乘客信息.csv')
titanic_info

sns.displot(x='乘客年龄', data=titanic_info,hue='乘客性别' )

4.Categorical中的常见图形

​ 我们今天只学习箱型图的相关知识和对应语法。

1.箱型图概念

​ 箱型图又称盒须图、盒式图或箱线图,它可以反映一组数据的分布特点,如分布是否对称,是否存在异常值,但其更适合对多组数据(分类数据)的分布特征进行比较。

​ 当一个数据集同时包含分类变量和连续变量时,箱形图可以提供连续变量随着分类变量改变而变化的信息。它使用了5个关键数值对分布进行概括,即一组数据的上相邻值、下相邻值、第一四分位数、中位数和第三四分位数。至于数据集中的异常值,则会被绘制成单独的点。

​ 通过观察箱型图,我们可以直观地了解到中位数,上相邻值和下相邻值这几个描述数据分布情况的关键值,也可以清晰地看到数据集中是否存在异常值。如下图所示,假如中位数在箱子中间位置的话,就说明这些数据是对称分布的;箱子的长短也能体现分布情况,箱子较长意味着分布比较分散,若箱子很短,则数据分布得较为集中。

2.箱型图语法

​ 值得注意的是,我们必须将控制图形类别的参数 kind 写为 kind='box',这样才能画出最基本的箱型图。kind 的其他值还可以是:‘strip’ 对应的分布散点图,‘swarm’ 对应的分簇散点图,‘violin’ 对应的小提琴图,‘boxen’ 对应的增强箱型图,‘point’ 对应的点图,‘bar’ 对应的条形图和 ‘count’ 对应的计数图。

​ 可以先将类型按照一定顺序存在列表中,再将这个列表传给参数 order,这样就可以让类别按照一定顺序从左到右依次排列了。

chart_df = pd.read_csv('./source-data/购物表.csv')
chart_df.head()
# 创建顺序列表 order_list
order_list=['配饰', '鞋子', '衣服', '日用品', '书籍', '食品']
# 将顺序列表传给参数 order
sns.catplot(x='类型', y='价格', data=chart_df, kind='box',order=order_list)

总结:

5.简单介绍轴级和图级

​ 上面我们讨论的所有图形,是 Seaborn 根据统计学知识这一维度进行分类的。其实 Seaborn 对于图形还有另一种分类的维度——是否将数据绘制到坐标轴上,按照此维度,Seaborn 的图形还可以划分为:axes-level(轴级)figure-level(图级)。我们刚才讲过的所有函数(relplot()、displot() 和 catplot())其实都属于 figure-level(图级),这些函数都可以通过改变参数kind 的值来画出不同的图形。

​ 简单介绍散点图、直方图和箱型图对应的 axes-level(轴级) 函数:

6.可视化库拓展

1.seaborn

可以继续阅读官方文档学习,其文档结构十分清晰;

​ 但是,Matplotlib 和 Seaborn 生成的图形,多有一种科技论文或学术期刊的风格,看起来有点严肃,而且都是些静态的图片,没法交互。

​ 所以,再简单介绍两个可交互的可视化库,有兴趣的话可以自学。

2.Plotly

​ Plotly 是著名的开源交互式绘图工具。它包含众多的图形种类,支持统计、金融、地理、科技、三维绘图等共计40余种交互式图表的绘制。默认样式生成的图片如下所示,可以发现,图片中是有交互栏的,更棒的是,当鼠标移动到图形上时,会显示出具体的数值,这比 Seaborn 要方便很多。Plotly 画出的图形配色生动,支持交互,又在图形上方添加了交互栏,总体来说,是一种比较优秀的可视化库。但其官方文档略微复杂,且网上的中文资料较少,自学起来可能会有一定的难度。

3.Pyecharts

​ Pyecharts是基于 Echarts 开发的一个用于生成 Echarts 图表的类库,同样支持交互操作。Echarts 是百度开源的一个数据可视化 JS 库,凭借着良好的交互性,精巧的图表设计,得到了众多开发者的认可。而Pyecharts,实际上就是 Echarts 与 Python 的对接,支持 30 多种图表。更重要的是,Pyecharts 的官方文档是由中文撰写的,自学起来没有什么障碍.

总结:

更多推荐

python数据分析&可视化