0%

Python数据分析

想要和得到中间还有两个字就是要做到

数据分析定义

matplotlib:帮助我们进行画图

numpy:处理数值型数组

pandas:在numpy基础之上,除了能处理数值型的数组之外,还能帮助处理字符串,时间序列,列表,字典等

  1. 为什么要学习数据分析

    能够非常方便的帮助我们从一堆数据里面找出直观的结论供来使用。

    是Python数据科学的基础。

    是机器学习课程的基础。

  2. 什么是数据分析

    数据分析是用适当的方法对收集来的大量数据进行分析,帮助人们作出判断,以便采取适当行动。

    数据分析流程:提出问题——准备数据——分析数据——获得结论——成果可视化

  3. 环境安装

    CONDA环境安装:

    CONDA集成了各种平台下所需要的环境对应的依赖,安装CONDA,对那种装不上的模块问题都可以轻松解决。

    1
    2
    3
    4
    5
    6
    7
    conda: data science package & environment manager
    创建环境
    conda create ——name python3 python=3
    切换环境
    windows:activate python3
    linux/macos:source activate python3
    官方地址:https://www.anaconda.com/download/

    anaconda安装【matplotlib折线图】06matplotlib绘制多次图形和不同图形的差异介绍和总结_哔哩哔哩_bilibili

  4. 认识jupyter notebook

matplotlib

作用:matplotlib能将数据进行可视化,更直观的呈现,是数据更加客观,更具有说服力。

定义:matplotlibs是最流行的Python底层绘图库,主要做数据可视化图表,名字取材于MATLAB,模仿MATLAB构建。

Python 使用 pip 安装 matplotlib 模块(精华版)_pip install matplotlib-CSDN博客

导入:from matplotlib import pyplot as plt

图片参数优化

  1. 设置图片大小(想要一个高清无码大图)

    1
    2
    3
    4
    fig=plt.figure(figsize=(20,8),dip=80)
    # figure图形图标的意思,在这里指的就是我们画的图
    # 通过实例化一个figure并且传递参数,能够在后台自动使用该figure实列
    # 在图形模糊的时候可以传入dpi(每英寸像素点的个数)参数,让图片更加清晰
  2. 保存到本地

    1
    2
    plt.savefig("./sig_size.png") # 当前位置下保存
    # 绘图后才可以保存,可以保存为svg这种矢量图格式,放大不会有锯齿
  3. 描述信息,比如x轴和y轴表示什么,图表示什么

    1
    2
    3
    4
    5
    6
    7
    8
    9
    plt.xlable("时间",fontproperties=my_font)
    plt.ylabel("温度 单位℃",fontproperties=my_font)
    plt.title("10点到12点每分钟温度的变化情况",fontproperties=my_font)
    # 注意中文问题
    # ---------------------------------
    # x轴刻度不是我们设置的,如何调整
    x=range(2,26,2)
    plt.xticks(x)
    # 步长变成0.5,如何调整
  4. 调整x或y的刻度的间距

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    from matplotlib import pyplot as plt
    # x轴表示时间,range()获取数据序列包前不包后
    x=range(2,26,2)
    # y轴表示温度
    y=[15,13,14,5,17,28,25,26,26,24,22,18]
    plt.plot(x,y) # 绘制折线图
    # 列表推导式(List Comprehension)是Python中的一种简洁而强大的语法,
    # 用于从一个已有的列表、元组、字典等可迭代对象创建新的列表。
    # 它允许你在一行代码中快速生成一个列表,具有更高的可读性和效率。
    _xticks=[i/2 for i in range(4,49)]
    plt.xticks(_xticks[::3]) # 绘制x轴上的刻度
    plt.yticks(range(min(y),max(y)+1))
    plt.show()
  5. 给图表添加网格

    1
    plt.grid(alpha=0.4) # alpha是透明度(0~1)
  6. 标注图例

    1
    2
    3
    4
    5
    6
    7
    8
    # 1.添加label
    plt.plot(x,y1,label="自己")
    plt.plot(x,y2,label="同桌")
    # 中文显示
    my_font = font_manager.FontProperties(fname="C:\\Windows\\Fonts\\方正粗黑宋简体.ttf") # 替换为您字体文件的路径
    # 2.添加图例
    plt.legend(prop=my_font,loc=0) # prop中文显示调用;loc表示图列位置
    # 3.调整图列位置

  7. 线条的样式(比如颜色,线条风格)

    1
    2
    plt.plot(x,y1,label="自己",color="green",linestyle=":")
    plt.plot(x,y2,label="同桌",color="orange",linestyle="——")

  8. 标记出特殊的点(比如告诉别人最高点和最低点在哪里)

  9. 给图片添加一个水印(防伪,防止盗用)

使用 matplotlib 绘图时,中文字符可能无法正确显示的问题

解决办法:可以通过matplotlib下的font_manager来修改matplotlib的默认字体

步骤:

  1. 查找系统中的中文字体

    1
    2
    3
    import matplotlib.font_manager as fm
    font_paths = fm.findSystemFonts(fontpaths=None, fontext='ttc')
    print(font_paths)

  2. 设置中文字体

    1
    2
    3
    from matplotlib import font_manager
    my_font = font_manager.FontProperties(fname="C:\\Windows\\Fonts\\方正粗黑宋简体.ttf") # 替换为您字体文件的路径
    plt.xticks(fontproperties=my_font) # rotation=45选择45°
  3. 显示结果

https://matplotlib.org/gallery/index.html

不同图形的应用场景

  • 折线图:以折线的上升或下降来表示统计数量的增减变化的统计图

​ 特点:能够显示数据的变化趋势,反应事物的变化情况(变化)

  • 直方图:由一系列高度不等的纵向条纹或线段表示数据分布的情况,一般用横轴表示数据范围,纵轴表示分布情况。

    特点:绘制连续性的数据,展示一组或者多组数据的分布状况(统计)

  • 条形图:排列在工作表的列或行中的数据可以绘制到条形图中。

    特点:绘制连离散的数据,能够一眼看出各个数据的大小,比较数据之间的差别(统计)

  • 散点图:用两组数据构成多个坐标点,考察坐标点的分布,判断两变量之间是否存在某种关联或总结坐标点的分布模式。

    特点:判断变量之间是否存在数量关联趋势,展示离群点(分布规律)

绘制散点图

1
2
# 绘制散点图
plt.scatter(x,y)

散点图的更多应用场景:

​ 不同条件(维度)之间的内在关联关系

​ 观察数据的离散聚合程度

绘制条形图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
_x = range(len(a))
_y = b
plt.bar(_x,b,width=0.2,color="orange")
—>bar绘制条形图,只能接收含数字的可迭代对象
->width表示长条的宽度,默认0.8
plt.xticks(_x,a,fontproperties=my_font,rotation=90)
->通过设置xticks实现数字和字符串的对应

# -------------------
# 绘制横向的条形图
plt.barh(range(len(a)),b,height=0.8)
plt.yticks(range(len(_x)),_x,rotation=45,fontproperties=my_font)
# 反转y轴,使得大的数据在上面
plt.gca().invert_yaxis()

条形图更多应用场景:频率统计

绘制直方图

一般来说能够使用plt.hist方法的是那些没有统计过的数据

直方图更多应用场景:用户年龄的分布状态;一段时间内用户点击次数的分布状态;用户活跃时间的分布状态。

通过条形图绘制直方图

【matplotlib常用统计图】05更多的绘图工具的了解_哔哩哔哩_bilibili

numpy

定义和创建

numpy:处理数值型数组,一个在Python中做科学计算的基础库,重在数值计算,也是大部分Python科学计算库的基础库,多用于在大型,多维数组上执行数值运算。

如何通过numpy创建数组(矩阵):

1
2
3
4
5
6
7
8
import numpy as np
# 使用numpy生成数组,得到ndarray的类型
a=np.array([1,2,3,4,5])
b=np.array(range(1,6))
c=np.arange(1,6)
# 上面a,b,c内容相同,注意arange和range的区别
# np.arange的用法:arange([start,] stop[,strp,],dtype=None)
# [1 2 3 4 5] <class 'numpy.ndarray'>

数组中存放数据的类型:

1
2
3
4
5
6
7
8
9
10
# 数据的类型
print(a.dtype)
#int64
# 指定创建的数组的数据类型
a=np.array([1,0,1,0],dtype=np.bool) #或者使用dtype='?'
# 修改数组的数据类型
a.astype("i1") #或者使用a.astype(np.int8)
# numpy中的小数
b=np.array([random.random() for i in range(10)])
b2=np.round(b,2)# 取小数操作(2位)

数组的形状

1
2
3
4
5
6
7
8
9
10
# 查看数组的形状
a.shape
# (num1,):表示一维数组的个数
# (num1,num2):表示二维数组的行列 :a.shape[0]*a.shape[1]获得二位数组数据的个数
# (num1,num2,num3):表示三维数组的块数,每块的行和列 :a.shape[1]*a.shape[2]获得三维数组数据的个数
# 修改数组的形状(重塑)
a.reshape((num,))
a.reshape((num1,num2))/a.arrange(12).reshape(3,4)
# 展开成一维数组的快捷方式
a.flatten()

数组的计算

数组和数字进行计算

​ 数组(+-/*)数字 (广播机制)

数组和数组进行计算

​ 数组+数组:对应位置进行计算

​ 行维度一致:行上进行运算

​ 列维度一致:列上进行运算

​ 行列维度均不一致: 不能进行运算

不同维度的任何方向上都不一样时才不能计算

1维数组,只有一个0轴;

2维数组(shape(2,2)),有0轴和1轴;

3维数组(shape(2,2,3)),有0,1,2轴。

numpy读取本地数据

​ csv:comma-separated value,逗号分隔值文件

1
2
3
4
5
np.loadtxt(frame.dtype=np.float,delimiter=None,skiprows=0,usecols=None,unpack=False)
# delimter:指定边界符合是什么,不指定会导致每行数据为一个整体的字符串而报错
# dtype:默认情况下对于比较大的数据会将其变成科学计数的方式
# upack:默认是False(0),默认情况下,有多少条数据就会有多少行
# 为True(1)的情况下,每一列的数据会组成一行,原始数据会有多少列,加载出来的数据就会有多少行,相当于转置的效果。

numpy索引和切片

  • 取行:t2[2]

  • 取连续多行:t2[2:,3]

  • 取不连续的多行:t2[[2,8,10]]
  • 取连续的多列:t2[:,2:]
  • 取不连续的多列: t2[:,[0,2]]
  • 取行和列:t2[2,3]
  • 取多行和多列,取第三行到第五行,第二列到第四列的结果(行列交叉点的位置):t2[2:5,1:4]
  • 取多个不相邻的点:t2[[0,2],[0,1]],(0,0)和(2,1)这两个点的值。

numpy中数值的修改

1
2
3
4
5
6
7
t=[[0,1,2,3,4,5],

[6,7,8,9,10,11],

[12,13,14,15,16,17],

[18,19,20,21,22,23]]

​ 用索引或者切片找到具体值,然后赋值。

where方法三元运算符:np.where(t<10,0,10)

1
2
3
4
5
6
7
t=[[0,0,0,0,0,0],

[0,0,0,0,10,10],

[10,10,10,10,10,10],

[10,10,10,10,10,10]]

clip方法裁剪:

1
2
3
4
5
6
7
8
9
t=[[0,1,2,3,4,5],
[6,7,8,9,10,11],
[12,13,14,15,16,17],
[18,19,20,nan,nan,nan]]
t.clip(10,18) #小于10的替换为10,大于18的替换为18
t=[[10,10,10,10,10,10],
[10,10,10,10,10,11],
[12,13,14,15,16,17],
[18,18,18,nan,nan,nan]]

数组的拼接

np.vstack((t1,t2))竖直拼接

1
2
3
4
5
6
7
8
9
10
11
t1=[[0,1,2,3,4,5],
[6,7,8,9,10,11]]
t2=[[12,13,14,15,16,17],
[18,19,20,21,22,23]]
np.vstack((t1,t2))
array[
[0,1,2,3,4,5],
[6,7,8,9,10,11],
[12,13,14,15,16,17],
[18,19,20,21,22,23]
]

np.hstack((t1,t2))水平拼接:

1
2
3
4
5
6
7
8
9
t1=[[0,1,2,3,4,5],
[6,7,8,9,10,11]]
t2=[[12,13,14,15,16,17],
[18,19,20,21,22,23]]
np.hstack((t1,t2))
array[
[0,1,2,3,4,5,12,13,14,15,16,17],
[6,7,8,9,10,11,18,19,20,21,22,23]
]

数组行列的交换

行交换

1
2
3
4
5
6
7
8
9
10
11
12
t=np.arange(12,24).reshape(3,4)
array([
[12,13,14,15],
[16,17,18,19],
[20,21,22,23]
])
t[[1,2],:]=t[[2,1],:]
array([
[12,13,14,15],
[20,21,22,23],
[16,17,18,19]
])

列交换

1
2
3
4
5
6
7
8
9
10
11
t=np.arange(12,24).reshape(3,4)
array([
[12,13,14,15],
[16,17,18,19],
[20,21,22,23]
])
t[:,[0,2]]=t[:,[2,0]]
array([
[[14 13 12 15]
[18 17 16 19]
[22 21 20 23]])

archive.zip

拓展方法

  1. 获得最大值最小值的位置

    np.argmax(t,axis=0)

    np.argmin(t,axis=1)

  2. 创建一个全0的数组

    np.zeros((3,4))

  3. 创建一个全1的数组

    np.ones((3,4))

  4. 创建一个对角线为1的正方形数组(方阵):np.eye(3)

numpy生成随机数

copy和view的注意点

  1. a=b完全不复制,a和吧相互影响(浅考贝)
  2. a=b[:],视图的操作,一种切片,会创建新的对象a,但a的数据完全由b保管,他们两个数据变化是一致的(浅考贝)
  3. a=b.copy(),复制,a和b互补影响(深拷贝)

numpy中的nan和inf

nan(NAN,NAN):not a number表示不是一个数字

什么时候numpy中会出现nan:

​ 当我们读取本地的文件为float的时候,如果有缺失,就会出现nan

​ 当做了一个不合适的计算的时候(比如无穷大(inf)-减去 无穷大)

inf(-inf,inf):infinity,inf表示正无穷,-inf表示负无穷

什么时候会出现inf包括(-inf,+inf)

​ 比如一个数字除以0,(python中直接会报错,numpy中是一个inf或者-inf)

注意点:

  1. 两个nan是不相等的(属于同一类但不是同等值)

    np.nan==np.nan

    False

  2. np.nan != np.nan

    np.nan!=np.nan

    True

  3. 利用以上特性,判断数组中的nan个数

    In [86]: t

    out[86]: array([1.,2.,nan])

    In [87]: np.count_nonzero(t!=t)

    Out[87]: 1

  4. nan和任何值计算都为nan

  5. 由于2特性,那么如何判断一个数字是否为nan呢?通过np.isnan(a)来判断,返回bool类型,比如希望把nan替换为0

    In[89]: t

    out[89]:array([1.,2.,nan])

    In[90]: np.isnan(t)(为nan返回True,否则返回 False)

    在一组数据中单纯把nan替换为0,会带来什么样的影响?

    ​ 比如,全部替换为0后,替换之前的平均值如果大于0,替换之后的均值肯定会变小。

    ​ 所以更一般的方式是把缺省的数值替换为均值(中值)或者是直接删除有缺失值的那一行

numpy中常用统计函数

求和:t.sum(axis=None)(None=0/1)

均值:t.mean(a,axis=None)受离群点的影响较大

中值:np.median(t,axis=None)

最大值:t.max(axis=None)

最小值:t.min(axis=None)

极值:np.ptp(t,axis=None),即最大值和最小值之差

pandas

定义

pandas:在numpy基础之上,除了能处理数值型的数组之外,还能帮助处理字符串,时间序列,列表,字典等。

numpy能够帮我们处理数值型数据,但是这还不够,很多时候,我们的数据除了数值之外,还有字符串,时间序列等。

比如:我们通过爬虫获取到了存储在数据库中的数据,之前youtobe的例子中除了数值之外还有国家的信息,视频的分类tag信息,标题信息等。

所以,numpy能够帮助我们处理数值,但是pandas除了数值之外(基于numpy),还能帮助我们处理其他类型的数据。

常用数据类型

  1. Series一维,带标签(索引)数组
  2. DataFrame二维,Series容器
1
2
3
4
5
6
import pandas as pd
import string
t1=pd.Series(np.arange(10),index=list(string.ascii_uppercase[:10]))
# 通过字典来创建
temp_dict={"name":"黄浩","age":30,"tel":10086}
t2=pd.Series(temp_dict)

Series的切片和索引

通过位置或索引取:

t1[“name"]或t1[1]

取连续的多行

t1[:2]

取不连续的多行

t1[[1,2]]

ndarray的很多方法都可以运用于series类型,比如argmax,clip。

series具有where方法,但是结果和ndarray不同。

读取外部数据

数据如果存储在csv中,直接使用pd.read_csv(),数据类型为DataFrame类型而不是Series类型

数据如果在数据库中(mysql或mongodb):pd.read_sql(sql_sentence,connection)

mongodb内容的读取

1
2
pip install pymongo
from pymongo import MongoClient

DataFrame

DataFrame对象既有行索引,又有列索引。

行索引,表明不同行,横向索引,叫index,0轴,axis=0

列索引,表明不同列,纵向索引,叫columns,1轴,axis=1

1
pd.DataFrame(np.arrange(12).reshape(3,4),index=list("abc"),columns=list("WXYZ"))

Series和DataFrame的关系:DataFrame是Series容器

a

DataFrame中排序的方法:

1
2
3
df=pd.read_csv("./dogName2.csv")
df=df.sort_values(by="Count_AnimalName",ascending=False)
print(df)

padas取行取列

方括号写数组,表示取行,对行进行操作df[:100]

写字符串,表示的取列索引,对列进行操作df["Cout_AnimalName"]

同时选择行和列如何操作?df[:100]["Cout_AnimalName"]

​ df.loc通过标签获取行数据

​ df.iloc通过位置获取行数据

pandas之布尔索引

df[df["Count_AnimalName"]>800]

多个布尔条件需要括号()括住每个条件并使用&/|符连接

1
2
3
4
5
6
		  Row_Labels  Count_AnimalName
1156 BELLA 1195
2660 CHARLIE 856
3251 COCO 852
9140 MAX 1153
12368 ROCKY 823

pandas缺失数据的处理

数据缺失通常有两种情况:

1. 空,None等,在pandas是NAN(和np,nan一样)
1. 另一种就是0

回顾numpy如何处理缺失数据:

​ 一般的方式是把缺省的数值替换为均值(中值)或者是直接删除有缺失值的那一行.

pandas处理缺失数据的方式:

NaN的处理

1
2
3
4
5
6
7
8
9
10
11
12
13
# 判断是否为NaN
pd.isnull(df)
pd.notnull(df)
# 处理方式1:删除NaN所在的行
dropna(axis=0,how='any',inplace=False)
# any表示有就删,all表示全部是NaN才删
# inplace表示是否进行原地修改
# ------------------------------------------
# 处理方式2:填充数据
t.fillna(t.mean()) #填充均值
t["列名"]=t["列名"].fillna(t["列名"].mean())#单独对某一列进行操作
t.fiallna(t.median()) #填充中值
t.fillna(0)

pandas在计算均值的时候不会去统计NaN的行,numpy就会计算。

为0数据的处理

1
2
3
t[t==0]=np.nan # 赋值为NaN
# 当然并不是每次为0的数据都需要处理
# 计算平均值等情况,nan是不参与计算的,但是0会的

数据的合并和分组聚合

重新构造一个全为0的数组,列名为分类,如果某一条数据中分类出现过,就让0变成1

字符串离散化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
df=pd.read_csv("./IMDB-Movie-Data.csv")
print(df["Genre"])
# 重新构造一个全为0的数组,列名为分类,如果某一条数据中分类出现过,就让0变成1
# 统计分类的列表
temp_list=df["Genre"].str.split(",").tolist()
genre_list=list(set([i for j in temp_list for i in j]))
# 构建全为0的数组
zeros_df=pd.DataFrame(np.zeros((df.shape[0],len(genre_list))),columns=genre_list)
# 给每个电影出现分类的位置赋值1
for i in range(df.shape[0]):
zeros_df.loc[i,temp_list[i]]=1
print(zeros_df)
# 统计每个分类的电影的数量和
genre_count=zeros_df.sum(axis=0)
# 排序
genre_count=genre_count.sort_values()
print(genre_count)
# 画条形图
plt.figure(figsize=(20,8),dpi=80)
_x=genre_count.index
print(_x)
_y=genre_count.values
plt.bar(range(len(_x)),_y)
plt.xticks(range(len(_x)),_x)
plt.show()

在pandas中类似的分组操作我们有很简单的方式来完成

grouped=df.group(by="columns_name")

DataFrameGroupBy 是 pandas 中的一种数据分组对象,通常是通过使用 groupby() 方法对 DataFrame 进行分组时返回的对象。通过 groupby(),你可以按照一个或多个列的值将数据划分成不同的组,并在每个组上执行聚合、变换或过滤操作。

grouped中的每一个元素是一个元组

元组里面是(索引(分组的值)),分组之后是DataFrame

非NA值(Non-NA Values)指的是那些在数据集中存在有效数据的值,即不是缺失值(NA)的数据。

1
2
3
4
5
6
7
# 按两个字段进行分组,生成的是Series类型(多索引)
grouped=df.groupby(by=[df["Country"],df["State/Province"]])
# 很多时候我们只希望对获取分组之后的某一部分数据,或者对某几列数据进行分组
# 获取分组之后的某一部分数据
df.groupby(by=["Country","State/Province"])["Country"].count()
# 对某几列数据进行分组
df["Country"].groupyby(by=[df["Country"],df["State/Province"]]).count()

想取一列,仍希望其是DataFrame时,可以使用[]嵌套[]的方式来完成。

索引和复合索引

简单的索引操作:

  • 获取index:df.index
  • 指定index:df.index=['x','y']
  • 重新设置index:df.reindex(list("abcdef"))
  • 指定某一列作为index:df.set_index("Country",drop=False)
  • 返回index的唯一值:df.set_index("Country").index.unique()

复合索引

Series的复合索引:x[“one”,”h”]或者x[“one”] [“h”]

当需要从内层索引里取值时:d.swaplevel()[]

DataFrame的复合索引:x=a.set_index([“c”,”d”])[[“a”]]

x.loc["one"] x.loc["one"].loc["h"] x.swaplevel().loc["h"]

如何取one和j对应的值: b.loc["one"].loc["j"]

数据合并之join&merge

join:默认情况下它是把行行索引相同的数据合并到一起。

merge:按照指定的列把数据按照一定方式合并到一起

默认的合并方式:inner,并集

merge outer,交集,NaN不全

merge left,左边为准,NaN补全

merge right,右边为准,NaN不全

pandas时间序列

1
2
# 生成一段时间范围
pd.date_range(start=None,end=None,periods=None,freq='D')

start和end以及freq配合能够生成start和end范围内以频率freq的一组时间索引

start和periods以及freq配合能够生成从start开始的频率为freq的periods个时间索引

在DataFrame中使用时间序列

如果时间列还不是 datetime 类型,可以使用 pd.to_datetime() 函数将其转换为 datetime 类型。这样 pandas 才能理解它并进行时间序列操作。

如果时间结果不是字符串如何处理PerioidIndex

1
2
3
4
5
# 把分开的时间字符串提供periodIndex的方法转化为pandas的时间类型
periods=pd.PeriodIndex(year=data["year"],month=data["month"],day=data["day"],
hour=data["hour"],freq="H")
df["datetime"]=periods
# 生成一个时间段

将时间列设置为索引

对时间序列数据进行分析时,通常需要将时间列设置为 DataFrame 的索引。这样可以方便地进行时间操作,如重采样、时间切片等。

1
df.set_index("timeStamp", inplace=True)

pandas重采样

重采样:指的是将时间序列从一个频率转化为另一个频率进行处理的过程,将高频率数据转化为低频率数据为降采样,低频率转化为高频率为升采样

pandas提供了一个resample的方法来帮助我们实现频率转化,它通常与聚合函数(如 sum()mean())一起使用。假设你的数据是按天记录的,你可以将其转换为按月统计的形式。

1
2
# 按月重采样并计算每个月的总和
monthly_data = df.resample("M").sum()