参考书
小飞蓬对本书选取重要知识点进行归总,并手动编写其中的代码
1 数据分析与挖掘区别
- 从定义说明:数据分析采用统计学方法,对获取的数据进行描述性和探索性分析,并从分析结论中发现数据间存在的价值,价值大多通过图的形式呈现。数据挖掘则是采用统计学,机器学习,人工智能的方法,对获取的数据,进行提炼、转化(类似于赌石),从数据中发掘出表象看不到的价值和规律
- 从工作侧重点:数据分析更侧重于实际业务,贴合业务展开分析工作,而数据挖掘更侧重于技术,通过更前进更换的算法,去提炼出更有价值的东西
- 从输出结果出来:数据分析更多是统计描述的呈现,比如总体样本的趋势。数据挖掘更多是模型和规则的输出
2 数据挖掘流程
3 python基础知识
数据结构及方法
列表
- 列表索引:正向索引、负向索引、切片索引、无限索引
- 列表操作:
元素增加:append、extend、insert
list=[1,10,100,1000,10000]
#append 在末尾添加数字
list.append(2)
print(list)
out:[1,10,100,1000,10000,2]
#extend在末尾添加多个元素
list.extend([20,200,2000,20000])
print(list)
out:[1,10,100,1000,10000,20,200,2000,20000]
#insert在指定位置插入新值
list.insert(2,11)
print(list)
out:[1,10,11,100,1000,10000]
元素删除:pop、remove、clear
list=[1,10,11,100,1000,10000]
#pop默认删末尾元素,也可以指定位置删除
list.pop()
print(list)
out:[1,10,11,100,1000]
list.pop(2)
print(list)
out:[1,10,100,1000,10000]
#remove删除指定值,但只能删除列表中第一次出现指定值
list.remove(11)
print(list)
out:[1,10,100,1000,10000]
#clear清空列表
list.clear()
print(list)
out:[]
- 列表计数、查询位置、逆转、排序
list=[7,3,9,11,4,6,10,3,7,4,4,3,6,3]
#计算列表元素3的个数
print(list.count(3))
#找出元素6所在位置
print(list.index(6))
#元素颠倒
print(list.reverse())
#列表元素降序
print(list.sort(reverse=True))
元组
元组与列表操作类似,但最大区别是元组是一种不变的类型的数据结构,简单说元组不能实现元素删除,修改,插入,但可以删除整个元组
字典
- 字典操作
字典元素增加:setdefault、update和键索引
dict={'姓名':'张三','年龄':33,'性别':'男','子女':{'儿子':'张四','女儿':'张美'},'兴趣':{'踢球','游泳','唱歌'}}
#增加元素
dict.setdefault('户籍','合肥')
dict.update(('学历':'硕士'))
dict['身高']=178
字典元素删除:pop、popitem、clear
#删除指定值
dict.pop('户籍')
dict['子女'].pop('女儿')
#删除字典中任意的一个元素
dict.popitem()
#清空元素
dict.clear()
字典其他方法:get(),keys(),values(),items()
#get从字典中取出键对应的值
print(dict.get('年龄'))
#keys取出所有键
print(dict.keys())
#values取出字典所有值
print(dict.values())
#items取出所有值对
print(dict.items())
字符串处理方法
字符串的常用方法
方法 | 使用说明 |
string[start: end :step] | 字符串的切片 |
string.split | 字符串分割 |
string.strip | 删除首尾空白 |
string.rstrip | 删除字符串右边空白 |
string.lstrip | 删除字符串左边空白 |
string.index | 返回子串首次出现的位置 |
string.replace | 字符串替换 |
sep.join | 将可迭代对象按sep分割符拼接为字符串 |
string.count | 对字符串 的子串计数 |
string.find | 返回子串首次出现位置 |
string.startwith | 字符串是否以什么开头 |
string.endwith | 字符串是否以什么结尾 |
正则表达式
符号 | 含义 | 示例 |
. | 可以匹配任意字符,但不包含换行符’\n’ | pyt.on~python |
\ | 转义符,一般用于保留字符中特殊元字符 | 10\ .3~10 .3 |
丨 | 逻辑或 | 人a丨A~人a或者人A |
[] | 用于匹配一组字符 | m[aA]n~man或者mAn |
\d与\D | \d匹配任意数字,\D代表非\d | 今天\d号~今天3号 |
\s与\S | \s匹配任意空白字符,\S代表非\s | 你\s好~你 好 |
\w与\W | \w匹配字母数字和下划线,\W代表非\w | p\wy~pay |
* | 匹配前一个字0到无穷次 | OK*~O或者OK或者OKK |
+ | 匹配前一个字符1到无穷次 | OK+~OK或者OKK |
? | 匹配前一个字符0到1次 | OK?~0或者OK |
{m} | 匹配前一个字符m次 | OK{3}~OKKK |
{m,n} | 匹配前一个字符m到n次 | OK{1,2}~OK或者OKK |
(.*?) | 用于分组,默认返回括号内匹配内容 |
- 匹配查询函数
findall(pattern,string,flags=0),获取字符串所有匹配的子串,并返回一个列表结果 - 匹配替换函数
sub(pattern,rel,string,count=0,flags=0),该函数根据正则表达式把满足匹配的内容替换为repl - 匹配分割函数
split(pattern,string,maxsplit=0,flags=0),按照指定正则表达式分隔字符
自定义函数
自定义函数可变参数(*args,**kwargs)
*args:可以接纳任意多个实参,接受多个实参,进行捆绑,并组装到元组中
**kwargs:可以把多个实参指定给各自的实参名
def adds(*args):
print(args)
s=sum(args)
return(s)
print('和为%d'%adds(10,13,7,8,2))
def info_collection(tel,birthday,**kwargs):
user_info={}
user_info['tel']=tel
user_info[birthday]=birthday
user_info.update(kwargs)
return(user_info)
info_collection(1214,'1990-11-11',nickname='yewe',gender='nan')
区别*args是用来发送一个非键值对的可变数量的参数列表给一个函数,**kwargs将不定长度的键值对,作为参数传递给一个函数
应用爬虫案例
项目目的为了获取城市的历史天气数据。字段包含日期、最低气温、最高气温、风向、风力、天气状况、空气质量指标值、空气质量等级和空气质量说明
# 导入第三方包
import requests
import time
import random
import pandas as pd
import re
# 生成请求头
headers = {
'Accept':'*/*',
'Accept-Encoding':'gzip, deflate',
'Accept-Language':'zh-CN,zh;q=0.9',
'Connection':'keep-alive',
'Cookie':'widget_dz_id=54511; widget_dz_cityValues=,; timeerror=1; defaultCityID=54511; defaultCityName=%u5317%u4EAC; Hm_lvt_a3f2879f6b3620a363bec646b7a8bcdd=1516245199; Hm_lpvt_a3f2879f6b3620a363bec646b7a8bcdd=1516245199; addFavorite=clicked',
'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3236.0 Safari/537.36'
}
# 生成所有需要抓取的链接
urls = []
for year in range(2011,2018):
for month in range(1,13):
if year <= 2016:
urls.append('http://tianqi.2345.com/t/wea_history/js/58362_%s%s.js' %(year,month))
else:
if month<10:
urls.append('http://tianqi.2345.com/t/wea_history/js/%s0%s/58362_%s0%s.js' %(year,month,year,month))
else:
urls.append('http://tianqi.2345.com/t/wea_history/js/%s%s/58362_%s%s.js' %(year,month,year,month))
urls
# 循环并通过正则匹配获取相关数据
info = []
for url in urls:
seconds = random.randint(3,6)
response = requests.get(url, headers = headers).text
ymd = re.findall("ymd:'(.*?)',",response)
high = re.findall("bWendu:'(.*?)℃',",response)
low = re.findall("yWendu:'(.*?)℃',",response)
tianqi = re.findall("tianqi:'(.*?)',",response)
fengxiang = re.findall("fengxiang:'(.*?)',",response)
fengli = re.findall(",fengli:'(.*?)'",response)
aqi = re.findall("aqi:'(.*?)',",response)
aqiInfo = re.findall("aqiInfo:'(.*?)',",response)
aqiLevel = re.findall(",aqiLevel:'(.*?)'",response)
# 由于2011~2015没有空气质量相关的数据,故需要分开处理
if len(aqi) == 0:
aqi = None
aqiInfo = None
aqiLevel = None
info.append(pd.DataFrame({'ymd':ymd,'high':high,'low':low,'tianqi':tianqi,'fengxiang':fengxiang,'fengli':fengli,'aqi':aqi,'aqiInfo':aqiInfo,'aqiLevel':aqiLevel}))
else:
info.append(pd.DataFrame({'ymd':ymd,'high':high,'low':low,'tianqi':tianqi,'fengxiang':fengxiang,'fengli':fengli,'aqi':aqi,'aqiInfo':aqiInfo,'aqiLevel':aqiLevel}))
time.sleep(seconds)
# 生成数据表
weather = pd.concat(info)
# 数据导出
weather.to_csv('weather.csv',index = False)
4.python数值计算工具-numpy
数组获取[rows,cols]
arr[[1,3],[1,3]]:
arr[np.ix_([1,3],[1,3])]
数据常用属性
在numpy模块汇总,可以通过genfromtxt函数读取外部文本文件数据
# 读入数据
stu_score = np.genfromtxt(fname = r'C:\Users\Administrator\Desktop\stu_socre.txt',delimiter='\t',skip_header=1)
# 查看数据结构
print(type(stu_score))
# 查看数据维数
print(stu_score.ndim)
# 查看数据行列数
print(stu_score.shape)
# 查看数组元素的数据类型
print(stu_score.dtype)
# 查看数组元素个数
print(stu_score.size)
数组的形状处理
方法:reshape、resize、ravel、flatten、vstack、hstack、row_stack、column_stack
reshape、resize都是对数组进行形状改变
reshape的改变只是预览,没有直接改变数组形状
resize方法不会返回预览,而是直接改变数组形状
如果需要将多维数组降维一维数组,利用ravel、flatten、reshape三种方法
arr4 = np.array([[1,10,100],[2,20,200],[3,30,300]])
print('原数组:\n',arr4)
# 默认排序降维
print('数组降维:\n',arr4.ravel())
print(arr4.flatten())
print(arr4.reshape(-1))
# 改变排序模式的降维
print(arr4.ravel(order = 'F'))
print(arr4.flatten(order = 'F'))
print(arr4.reshape(-1, order = 'F'))
vstack,row_stack纵向堆叠数组
hstack,column_stack横向堆叠数组
线性代数相关计算
函数 | 说明 |
np.zeros | 生成零矩阵 |
np.eyes | 生成单位矩阵 |
np.dot | 数组点积 |
np.diag | 矩阵主对角线与一维数组间的转化 |
np.ones | 生成所有元素为1矩阵 |
np.transpose | 矩阵转置 |
np.inner | 计算两个数组内积 |
np.trace | 矩阵主对角线元素的和 |
np.linalg.det | 计算矩阵行列式 |
np.linalg.eig | 计算矩阵特征根与特征向量 |
np.linalg.eigvals | 计算方阵特征根 |
np.linalg.inv | 方阵逆 |
np.linalg.pinv | 计算伪逆 |
np.linalg.solve | 计算Ax=b的线性方程组的解 |
np.linalg.lstsq | 计算Ax=b的最小二乘解 |
np.linalg.qr | QR分解 |
np.linalg.svd | 奇异值分解 |
np.linalg.norm | 计算向量或矩阵的范数 |
python数据处理工具-pandas
pandas对应的数据构造和数据读取这里不再阐述
数据类型转换及描述统计
通常拿到数据后,观看数据的shape以及的dtypes
修改数据类型,数值类型修改,时间类型修改
对数值类型数据进行统计描述,对离散型数据进行统计描述
# 数据类型转换及描述统计
# 数据读取
sec_cars = pd.read_table(r'C:\Users\Administrator\Desktop\sec_cars.csv', sep = ',')
# 预览数据的前五行
sec_cars.head()
# 查看数据的行列数
print('数据集的行列数:\n',sec_cars.shape)
# 查看数据集每个变量的数据类型
print('各变量的数据类型:\n',sec_cars.dtypes)
# 修改二手车上牌时间的数据类型
sec_cars.Boarding_time = pd.to_datetime(sec_cars.Boarding_time, format = '%Y年%m月')
# 修改二手车新车价格的数据类型
sec_cars.New_price = sec_cars.New_price.str[:-1].astype('float')
# 重新查看各变量数据类型
sec_cars.dtypes
# 数据的描述性统计
sec_cars.describe()
# 数据的形状特征
# 挑出所有数值型变量
num_variables = sec_cars.columns[sec_cars.dtypes !='object'][1:]
# 自定义函数,计算偏度和峰度
def skew_kurt(x):
skewness = x.skew()
kurtsis = x.kurt()
# 返回偏度值和峰度值
return pd.Series([skewness,kurtsis], index = ['Skew','Kurt'])
# 运用apply方法
sec_cars[num_variables].apply(func = skew_kurt, axis = 0)
# 离散型变量的统计描述
sec_cars.describe(include = ['object'])
# 离散变量频次统计
Freq = sec_cars.Discharge.value_counts()
Freq_ratio = Freq/sec_cars.shape[0]
Freq_df = pd.DataFrame({'Freq':Freq,'Freq_ratio':Freq_ratio})
Freq_df.head()
# 将行索引重设为变量
Freq_df.reset_index(inplace = True)
Freq_df.head()
字符与日期数据的处理
数据处理:
- 改变出生日期birthday和手机号tel两个字段的数据类型
- 根据出生日期birthday和开始工作日期start_work两个字段新增年龄和工龄两个字段
- 将手机号tel的中间四位隐藏
- 根据邮箱信息新增邮箱域名字段
- 基于other字段取出每个人员的专业信息
# 数据读入
df = pd.read_excel(r'C:\Users\Administrator\Desktop\data_test03.xlsx')
# 各变量数据类型
print(df.dtypes)
# 将birthday变量转换为日期型
df.birthday = pd.to_datetime(df.birthday, format = '%Y/%m/%d')
# 将手机号转换为字符串
df.tel = df.tel.astype('str')
# 新增年龄和工龄两列
df['age'] = pd.datetime.today().year - df.birthday.dt.year
df['workage'] = pd.datetime.today().year - df.start_work.dt.year
# 将手机号中间四位隐藏起来
df.tel = df.tel.apply(func = lambda x : x.replace(x[3:7], '****'))
# 取出邮箱的域名
df['email_domain'] = df.email.apply(func = lambda x : x.split('@')[1])
# 取出用户的专业信息
df['profession'] = df.other.str.findall('专业:(.*?),')
# 去除birthday、start_work和other变量
df.drop(['birthday','start_work','other'], axis = 1, inplace = True)
df.head()
# 常用日期处理方法
dates = pd.to_datetime(pd.Series(['1989-8-18 13:14:55','1995-2-16']), format = '%Y-%m-%d %H:%M:%S')
print('返回日期值:\n',dates.dt.date)
print('返回季度:\n',dates.dt.quarter)
print('返回几点钟:\n',dates.dt.hour)
print('返回年中的天:\n',dates.dt.dayofyear)
print('返回年中的周:\n',dates.dt.weekofyear)
print('返回星期几的名称:\n',dates.dt.weekday_name)
print('返回月份的天数:\n',dates.dt.days_in_month)
数据清洗方法
重复观测处理
df.drop_duplicates(inplace=True)
缺失值处理
当遇到缺失值NaN表示时,采用三种方法处置:
- 替换法:均值、中位数替换
- 删除法:当缺失数据比例在5%以内
- 插补法:回归插补法、K近邻插补、拉格朗日插补
# 数据读入
df = pd.read_excel(r'C:\Users\Administrator\Desktop\data_test05.xlsx')
# 缺失观测的检测
print('数据集中是否存在缺失值:\n',any(df.isnull()))
# 删除法之记录删除
df.dropna()
# 删除法之变量删除
df.drop('age', axis = 1)
# 替换法之前向替换
df.fillna(method = 'ffill')
# 替换法之后向替换
df.fillna(method = 'bfill')
# 替换法之常数替换
df.fillna(value = 0)
# 替换法之统计值替换
df.fillna(value = {'gender':df.gender.mode()[0], 'age':df.age.mean(), 'income':df.income.median()})
异常值处理
对于异常值检测,采用两种方法:
- n个标准差法
- 箱线图判别法
数据子集获取
iloc、loc、ix(它们的语法可以表示成[row_select,cols_select])
name | gender | age | |
0 | 张三 | 男 | 23 |
1 | 李四 | 女 | 25 |
2 | 王二 | 女 | 23 |
3 | 丁一 | 女 | 21 |
4 | 李五 | 男 | 20 |
df1=pd.DataFrame({'name':['张三','李四','王二','丁一','李五'],
'gender':['男','女','女','女','男'],
'age':[23,25,23,21,20],columns=['name','gender','age']})
df1.iloc[1:4,[0,2]]
df1.loc[1:3,['name','age']]
df1.ix[1:3,[0,2]]
name | age | |
1 | 李四 | 25 |
2 | 王二 | 23 |
3 | 丁一 | 21 |
透视表功能
生成透视表函数
pd.pivot_table(data,values=None,index=None,columns=None,aggfunc='mean',fill_value=None,margins=False,dropna=True,margins_name='All')
- data:指定需要构造透视表的数据集 values:指定需要拉入的数值的字段列表
- index:指定拉入的行标签的字段列表
- columns:指定拉入列标签的字段列表
- aggfunc:指定数值统计函数
- fill_value:指定一个标量。用于填充缺失值
- margins:bool类型参数,是否需要显示行或列的总计值
- dropna:bool参数,是否需要删除整列缺失字段
- margins_name:指定行或列的总计名称
挖掘算法实践
线性回归模型
一元线性回归模型
目标函数公式:
关于目标函数求导:
1.设偏导数为0
2.公式转化
3.化解
4.求解a、b
代码实现
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# 导入数据集
income = pd.read_csv(r'C:\Users\Administrator\Desktop\Salary_Data.csv')
# 绘制散点图
sns.lmplot(x = 'YearsExperience', y = 'Salary', data = income, ci = None)
# 显示图形
plt.show()
# 简单线性回归模型的参数求解
# 样本量
n = income.shape[0]
# 计算自变量、因变量、自变量平方、自变量与因变量乘积的和
sum_x = income.YearsExperience.sum()
sum_y = income.Salary.sum()
sum_x2 = income.YearsExperience.pow(2).sum()
xy = income.YearsExperience * income.Salary
sum_xy = xy.sum()
# 根据公式计算回归模型的参数
b = (sum_xy-sum_x*sum_y/n)/(sum_x2-sum_x**2/n)
a = income.Salary.mean()-b*income.YearsExperience.mean()
# 打印出计算结果
print('回归参数a的值:',a)
print('回归参数b的值:',b)
# 导入第三方模块
import statsmodels.api as sm
# 利用收入数据集,构建回归模型
fit = sm.formula.ols('Salary ~ YearsExperience', data = income).fit()
# 返回模型的参数值
fit.params
多元线性回归模型
目标函数公式:
关于目标函数求导:
1.求偏导为0
2.计算偏回归系统值
回归模型的假设检验
模型的显著性检验-F检验1.提出假设
H0为原假设,假设认为模型的所有偏回归系统全为0,认为没有一个自变量可以构成因变量的线性组合,H1为备选假设,与原假设对立。
2.构造统计量
其中p和n-p-1分别为RSS和ESS的自由度,模型拟合越好,ESS就会越小,RSS则会越大,F统计量也就越大。
KNN模型应用
算法思想:
- 确定未知样本近邻的个数k值
- 根据某种度量样本间相似度的指标(如欧式距离)将每个未知类别的样本的最近k个已知样本搜寻出来,形成一个个簇
- 对搜寻出来的已知样本进行投票,将各簇下类别最多的分类用作未知样本点的预测
最佳k值选择:
- 设置k近邻样本投票权重
- 采用多重交叉验证,将选择k个不同的值,在每种值下执行m重交叉验证,最后选出平均误差最小的k值
相似度度量方法
- 欧式距离
- 曼哈顿距离
- 余弦相似度
- 杰卡德相似系数
朴素贝叶斯模型
SVM模型应用
GBDT模型应用
待更新。。。。