数据分析(Numpy,Pandas,Matplotlib)
一、数分基础
1、基本流程
数据收集->数据清洗->数据分析->数据可视化
2、Jupyter Notebook
二、Numpy
1、ndarray的性质
特性:多维性、同质性、高效性
[1,'Hello'] # 这样得到的array里的 1 是字符串
属性:
2、创建
arr = np.array([1,2,3],dtype=float64) # 要求元素类型为浮点型 arr2 = np.copy(arr) arr = np.zeros((2,3),dtype = int) # 创造一个2行3列的0矩阵 arr = np.zeros((200,),dtype = int) # 创造一个一维200个0的数组 arr = np.ones((2,3),dtype = int) # 创造一个2行3列的1矩阵 arr = np.empty((2,3)) # 创造一个2行3列的未定义矩阵 arr = np.full((2,3),2025) # 创造一个2行3列的元素全为2025的矩阵 arr2 = np.zeros_like(arr) # 创造与arr形状相同的0矩阵 arr2 = np.empty_like(arr) # 创造与arr形状相同的未定义矩阵 arr2 = np.ones_like(arr) # 创造与arr形状相同的1矩阵 arr2 = np.full_like(arr,2026) # 将arr元素全替换为2026
高级
arr = np.arrange(1,10,1) # 从1到10 以步长为1的等差数列,左闭右开 1 ~ 9 arr = np.linspace(1,10,5 # 1 到 10之间等间隔地取5个数字 arr = np.logspace(0,4,2,2) # 从0 到 4等间隔取两个以2为底的数字 [1,16] arr = np.logspace(0,4,3,2) # 从0 到 4等间隔取两个以2为底的数字 [1,4,16]
特殊矩阵
arr = np.eye(3) # 生成一个秩为3的单位阵 arr = np.eye(3,4) # 生成一个三行四列的[E|0] arr = np.diag([1,2,3]) # 生成特征值为1,2,3的对角阵 arr = np.random.rand(2,3) # 生成一个范围为0~1的2行3列的随机浮点数组 arr = np.random.uniform(3,6,(2,3)) # 生成一个指定范围为 3~6 的2行3列的随机浮点数组 arr = np.random.randint(3,6,(2,3)) # 生成一个指定范围为 3~6 的2行3列的随机整数数组 arr = np.random.randn(2,3) # 生成一个2行3列的正态分布数组 np.random.seed(20) # 设置种子可以保证下方的random生成的数组是固定的 arr = np.random.randint(3,6,(2,3))
3、数值类型
4、索引与切片
一维
arr = np.random.randint(1,100,20) print(arr[10]) print(arr[:]) # 获取全部数据 print(arr[2:5]) # 获取下标为2~5的左闭右开的数据 print(arr[slice(2,15,3)]) # 与:等价 start,end,step print(arr[(arr>10) | (arr < 70)]) # 获得所有大于10小于70的元素,通过布尔条件筛选满足条件的元素,支持逻辑运算符
二维
print(arr[1,3]) print(arr[:,:]) # 获得全部 print(arr[1,2:5]) # 取得第1行第2到4列 (都是下标) print(arr[arr>50]) # 返回的是一维的 print(arr[2][arr[2] > 50]) print(arr[:,3]) # 取得第三列 print(arr[2,:]) # 取得第二行
5、运算
a = np.array([1,2,3]) b = np.array([4,5,6]) print(a + b) # [5 7 9] c = np.array([[1,2,3],[4,5,6]]) print ( c + 3 ) # 每个元素都加3
# 广播机制 # 可广播条件:两个矩阵行和列分别看是否相同或是否含一个1 a = np.array([1,2,3]) # 1 * 3 b = np.array([[4],[5],[6]]) # 3 * 1
矩阵乘法运算
print(a @ b) # 矩阵乘法
6、常用函数
(1)、数学函数
arr = np.array([1,2,3]) print(np.sqrt([1,4,9])) # 开平方根,返回浮点数 print(np.exp([1,2,3])) # 计算指数 e^x print(np.log(2.71)) # 计算对数 lnx print(np.sin(np.pi)) # sinx print(np.abs(arr)) # 绝对值 print(np.power(arr,2)) # 幂 print(np.round([3.2,4,5])) # 四舍五入 print(np.ceil(arr)) # 向上取整 print(np.floor(arr)) # 向下取整 arr = ([1,2,3,np.nan]) print(np.isnan(arr)) # 检测缺失值,[False,False,False,True]
(2)、统计函数
arr = np.array([1,2,3,4,5]) print(np.sum(arr)) # 求和 print(np.mean(arr)) # 平均值 print(np.median(arr)) # 中位数,排序后 print(np.var(arr)) # 标准差 print(np.std(arr)) # 方差 print(np.max(arr)) # 最大值 print(np.min(arr)) # 最小值 print(np.percentile(arr,50)) # 分位数 , 取得50% 的数 print(np.cumsum(arr)) # [1,2,3]累和 [1,3,6] print(np.cumprod(arr)) # 累积 [1,2,6]
(3)、比较函数
arr = np.array([1,2,3,4,5]) print(np.great(arr,4) # 是否大于 print(np.less(arr,4) # 是否小于 print(np.equal(arr,4) # 是否等于 print(np.equal(arr,arr) # 可以广播
print(np.logical_and([1,0],[0,1])) # 与 print(np.logical_or([1,0],[0,1])) # 或 print(np.logical_not([1,0],[0,1])) # 非
print(np.where(arr<3,arr,0)) # [1,2,0,0,0] print(np.where( score<60,'不及格',np.where( score<80,'良好','优秀' ) ))
(4)、排序函数
arr = np.array([1,2,3]) arr.sort() print(arr) print(np.sort(arr)) # 排序 print(np.argsort(arr)) # 打印出排序后的索引 print(np.unique(arr)) # 去重 print(np.concatenate((arr1,arr2))) # 数组拼接 print(np.split(arr,4)) # 数组分割(等分份数) print(np.split(arr,[6,12,18])) # 给定分割位置,索引为6的元素前面切一刀 print(np.reshape(arr,[2,10])) # 调整数组形状
三、Pandas
1、series
创建
import pandas as pd s = pd.Series([1,2,3,4,5],index=['A','B','C','D','E'],name = '月份') s = pd.Series({"a":1,"b":2,"c":3,"d":4,"e":5})
属性
print(s.index) print(s.values) print(s.shape,s.ndim,s.size,s.dtype,s.name) print(s.loc['a':'c']) # 显式索引,按标签索引或切片 print(s.iloc[1]) # 隐式索引,按位置索引或切片 print(s.at['a']) # 不可切片
访问
print(s['a']) print(s[s<3]) # 筛出元素小于3
方法
print(s.head(3)) # 默认前五个,tail后五个 s.describe() # 查看所有的描述性信息 print(s.count()) # 元素个数 print(s.keys()) # s.index print(s.isna()) # 判断是否为缺失值 print(s.isin(4,5)) # 判断是否含 4、5 s.quantile(0.25) # 分位数 print(s.mode()) # 众数 print(s.value_counts()) # 计算每一个元素频率 s.drop_duplicates() # 去重 ,得到series数据类型 s.unique() # 得到list类型 print(s.nunique()) # 去重后的元素个数 s.sort_values() # 排序 按values s.sort_index() # 排序 按index
2、DataFrame
一列就是一个series,每一列数据类型相同,不同列可不同
创建
# 通过Series创建 s1 = pd.Series([1,2,3,4]) s2 = pd.Series([5,6,7,8]) df = pd.DataFrame({"第1列":s1,"第2列":s2}) # 通过字典创建 df = pd.DataFrame( { "id":[1,2,3,4,5], "name":["tom","jack","rose","alice","kobe"], "age":[15,16,18,24,0], },index = [1,2,3,4,5],columns=["name","score","age"] )
属性
print(df.index,df.columns,df.values) # 行索引和列标签、值 print(df.T) # 转置 print(df.loc[4]) # 显式索引 print(df.iloc[3]) # 隐式索引 print(df.loc[:,'name']) print(df.iloc[:,0]) print(df.at[3,'score']) print(df.iat[2,1])
访问
print(df['name','score']) df[(df['score']>70) & (df.age<20)] df.sample() # 随机抽样
方法
print(df.isin(['jack',20])) # 是否存在 print(df.isna(['jack',20])) # 是否是缺失值 print(df['score'].sum()) print(df.value_counts()) # 每条信息出现的次数 print(df.drop_duplicates()) # 去重 print(df.duplicated(subset=['name'])) # 检查name列是否重复 print(df.replace(15,30)) # 替换 print(df.cumsum()) # 累和 print(df.cummax()) # 累积最大 print(df.sort_index(ascending=False)) # 按索引 ascending 是否升序,默认ascend print(df.sort_values(by=['score','age'],ascending=[False,True])) # 按values排序,先降序score排序,再升序age排序 df.nlargest(2,columns=['score','age']) # 取成绩最高的两条数据 df.nsmallest(2,columns=['score','age'])
四、数据分析
1、数据收集
df = pd.read_csv('data/employees.csv') df df.to_csv('new_profile.csv') # 导出 # pandas只能读取简单的json文件 import json with open('data/test.json') as f: data = json.load(f) print(data['users']) df = pd.DataFrame(data['users']) # 较为复杂的json格式是一个嵌套字典,将key传入可以分别得到users和用户的列表 print(type(df)) df
2、数据清洗与分析
(1)、查找缺失值
s = pd.Series([1,2,None,pd.NA,np,nan]) df = pd.DataFrame([1,pd.NA,2][2,3,5][None,4,6]) print(s) print(s.isma()) print(s.isnull) print(df.isna()) print(df.isnull()) print(df.isna().sum(axis=1)) # 查看缺失值个数,默认按列索引,axis=1改为行索引 print(s.dropna()) # 删除缺失值,去除含有缺失值的整行 print(s.dropna(how = 'all')) # 如果所有的值为缺失值则删除该条数据 print(s.dropna(thresh=2)) # 至少有n个值不是缺失值就保留 print(s.dropna(axis=1)) # 剔除一整列,默认一行为一条数据,axis=1改为一列为一条数据 print(s.dropna(subset=['第1列'])) # 如果 '第1列' 列有缺失值则去掉整行数据
以下分别是数据的格式和 isna() dropna() 的默认情况,也就是axis = 0
(2)、填充缺失值
print(df.fillna({'temp_max':20,'waind':2.5}))\ print(df.fillna(df[['wind']].mean())) print(df.ffill()) # 使用缺失值前面的值填充 print(df.bfill()) # 使用缺失值后面的值填充
为什么df[['wind']] 要加两个 []?
df['wind']
→ 返回一个 Series,
df[['wind']]
→ 返回一个 DataFrame
df[['wind']].mean()
返回的是一个 DataFrame 的列向量(即单列 DataFrame),这样
.fillna()
才能按列对齐地填充缺失值。
(3)、数据类型转换
df.duplicated() # 一整条记录都是一样的,标记为重复,返回为true df.drop_duplicates(subset=['name']) # 根据指定的name列去重 df.drop_duplicates(subset=['name'],keep='last') # 保留最后出现的 df['age'] = df['age'].astype('int16') # 将age的int64变为int16 df['gender'] = df['gender'].astype('category') #将gender变为类别 df['is_male'] = df['gender'].map({'Male':True,'Female':False})
(4)、数据变形
将下面的数据变形成上面的数据
# 宽表转长表 df2 = pd。melt(df,id_vars=['ID','name'],var_name='科目',value_name='成绩') # ID 和name不变 , var_name 为column名字,value_name 为value名字 df2.sort_values('name')
# 长表转宽表 pd。pivot(df2,index=['ID','name'],colunms='科目',values='成绩')
# 数据分裂,将姓名分开 df = pd.DataFrame(data) df[['first','last']] = df['name'].str.split(" ",expand=True) # 这一步操作可以学到很多东西,将df的name列按空格分开,expand=True将分开后的列表拆开,df[[]] 将分开的两列分别作为series写入df(DataFrame)的两列 df[['high','low']] = df['blood_pressure'].str.split('/',expand=True) df['high'] = df['high'].astype('int64') # 类型转换后注意都是字符型,如果需要对数据统计要转换成整形或浮点型
(5)、数据分箱
pd.cut(df1['salary'],bins=2) # cut将数据划分区间 bins=n,分成n段区间,起始值和结束值为最小和最大值 pd.cut(df1['salary'],bins=2).value_counts() # 统计 pd.cut(df1['salary'],bins=[0,10000,20000]).value_counts() # 指定区间 df1['收入范围'] = pd.cut(df1['salary'],bins=[0,10000,20000],lables=['低','中','高']) pd.qcut(df['salary'],3) # 均分高低中收入人数
# 睡眠数据 df = pd.read_csv('data/sleep.csv') df1 = df.head(10)[['person_id','sleep_quality']] df['睡眠质量'] = pd.cut(df['sleep_quality'],bins=3,labels=['差','中','优']) # 数值---》分箱---》统计 df['睡眠质量'].value_counts() df['gender'] = df['gender'].astype('catogory') # 字符串---》类别---》统计 df['gender'].value_counts()
(6)、其他操作
df = pd.DataFrame( 'name':['jack','rose'], 'age':['12','3'], 'gender':['male','female'], ) df.set_index(name,inplece=True) # 将name变为索引,inplace在当前df上进行操作 df.reset_index(inplace=True) # 变回去 df.rename(columns={'age':'年龄'},index={0:4}) # 修改列名 df.index=[1,2,3,4] # 直接设置索引
3、时间数据
(1)、基础操作
d = pd.Timestamp('2025-05-02 10:22') print(d) print(d.year,d.month,d.day,d.hour,d.minute,d.second,d.quarter,d.is_month_end) # quarter为季度,is_month_end 是否为月底 print(d.day_name) # day_name星期几 print(d.to_period('D')) # D转化为天 Q转化为季 Y转化为年 M转化为月 W转化为周
# 数据类型转化为Timestamp a = pd.to_datetime('2015-02-28 10:22') # 字符串类型转化为日期 df = pd.DataFrame({ 'sales':[100,200,300], 'date':['20250601','20250602','20250603'] }) df['datetime'] = pd.to_datetime(df['date']) df['week'] = df['datetime'].dt.day_time() # df['datetime'] 取出来的是Series,需要用.dt转化为DateTime数据类型
(2)、其他操作
df = pd.read_csv('data/weather.csv',parse_dates=['date']) # 把date这一列自动解析成日期类型
# 其他操作 df.set_index('date',inplace=True) # 将日期设为索引 print(df.loc['2013-01':'2013-02']) # 取数据就很方便了 d1 = pd.Timestamp('2013-01-15') d2 = pd.Timestamp('2023-01-15')\ d3 = d2 - d1 # 时间间隔 df['delta'] = df['date'] - df['date'][0] # 算出时间间隔 df.set_index('delta',inplace=True) days = pd.date_range("2025-07-03","2026-02-09",freq="W") # 每周每年每季 DWYM 同上 YE年底 YS年初
(3)、重新采样
df[["temp_max","temp_min"]].resample("YE").mean() # 重新采样,之前的数据是每天,将数据变为每周每月每年则为重新采样,可以看到 df[[]] 出来的就是一个新的dataframe df[] 就是增添新的column
4、分组聚合
df.groupby('department_id').groups # 查看分组 df.groupby('department_id').get_group(10) # 得到department_id 为10的组 df2 = df.groupby('department_id')[['salary']],mean() # 以department_id分组,计算每组的salary平均值 (series很丑,用[[]]) df2['salary'] = df2['salary'].round(2) # 四舍五入 df2.reset_index(inplace=True) df2.sort_values('salary',ascending=False) df.groupby(['department_id','job_id']) # 每个部门每个岗位
五、Matplotlib
1、折线图
import matplotlib.pyplot as plt from matplotlib import rcParams # 字体 rcParams['font.family'] = 'SimHei' plt.figure(figsize=(10,5)) # 设置纸张大小 month = ['1月','2月','3月','4月'] sales = [100,200,300,400] plt.plot(month,sales,label='产品A') # 绘制折线图 plt.title('2025年销售趋势',color=red,fontsize=20) # 标题 plt.xlabel('月份',fontsize = 10) # 坐标轴标签 plt.ylabel('销售额(万元)',fontsize = 10) plt.legend(loc = 'upper left') # 添加图例,左上角那个 plt.grid(True,alpha=0.5,color=blue,linestyle='--') # 背景添加网格线,True x y 两条轴都有 , alpha设置透明度 linestyle 设置线格式 plt.grid(axis='x') # 只有从x轴发出来的线 plt.xticks(rotation=0,fontsize=12) # 设置刻度大小 rotation 旋转角度 plt.xticks(rotation=0,fontsize=12) plt.ylim(0,160) # 设置y轴范围 for x,y in zip(month,sales): print(x,y) plt.text(x,y+1,str(y),ha='center',va='bottom')# 在每个数据点上显示数值,ha水平方向,va竖直方向 plt.show() # 显示
2、条形图
# 大部分步骤与折线图相同 subjects = ['语文','数学','英语'] scores = [54,78,89] plt.bar(subjects,scores,lable = '小明') # 绘制柱状图 plt.barh(subjects,scores,lable = '小明') # 条形图
3、饼图
subjects = ['语文','数学','英语'] times = [5,2,6] explode=[0.1,0,0] # 让语文突出去 plt.pie(times,labels=subjects,autopct='%.1f',startangle=90,colors=['#66b3ff','#99ff99','#ffcc99'],wedgeprops={'width':0.6},pctdistance=0.6,explode=explode) # autopct 显示百分号保留一位小数startangle 起始角度 wedgeprops 设置圆环 pctdistance 百分比距离圆心距离 explode设置爆炸式饼图
4、散点图
x=[] y=[] for i in range(1000): tmp = random.uniform(0,10) x.append(tmp) y.append(2*tmp + random.gauss(0,2)) # 绘制 plt.scatter(hours.scores,s=20) # s设置原点大小
##