了解数据
数据收集及读取
数据收集
- 数据接口 -- 一些网站有API接口,通过相应的API请求方式就能获取到想要的数据,一般情况下数据会非常规范,完整。
- 数据库 -- 需要得到授权。
- 数据爬虫 -- 优势在于你无需数据拥有者提供权限,缺点是获取到的数据特征有限,往往会有更多丢失或错误数据。
数据读取
1.数据文件读取
数据文件,最常见到的有.csv, .txt,少见有.xlsx格式的文件。对于存在本地的.csv, .txt数据文件,首先用open()方法:
file = open("xxx.csv", 'r')
for line in file:
print(line)
对于excel支持的.xlsx文件需要安装xlrd包:
sudo pip3 install xlrd
import xlrd
file = xlrd.open_workbook("xxx.xlsx")
# 按索引读取表
table = file.sheet_by_index(0)
# 读取每行并打印
for i in range(table.nrows):
print(table.row_values(i))
open方法的局限很多。所以大多数情况下都会数据预处理神器Pandas来完成数据文件的读取工作。
import pandas as pd
df = pd.read_csv("xxx.csv")
print(df)
使用Pandas读取文件方便,代码简洁,读取的数据直接就是DataFrame。
除了csv文件,Pandas还可以读取其他文件:
# JSON文件
pd.read_json
# HTML文件
pd.read_html
# 本地剪切板
pd.read_clipboard
# MS Excel文件
pd.read_excel
# HDF5Format文件
pd.read_hdf
# Feather 格式
pd.read_feather
# Msgpack
pd.read_msgpack
# Stata
pd.read_stata
# SAS
pd.read_sas
# Python Pickle格式
pd.read_pickle
# SQL数据库
pd.read_sql
# Google Big Query
pd.read_gbq
对于Pandas读取数据方法,还带有很多参数。比如txt格式的文件:
df = pd.read_table("xxx.txt", header=0, sep='\t')
print(df)
这里,header=0表示将第一行设为表头,而sep='\t'则代表使用空格分隔字段。
2.数据库读取
除了数据文件,如果想要读取 MySQL,Oracle,Microsoft SQL 等数据库里面的数据。一般情况下都需要安装相应的第三方库。例如,读取 MySQL 时,我们需要安装 MySQLdb:
sudo apt-get install python-dev libmysqlclient-dev
sudo pip install MySQL-python
安装之后启动mysql服务:
sudo service mysql start
然后使用root用户登陆:
mysql -u root -p
向数据库写入一些数据:
# 创建名为xxx的数据库
mysql> CREATE DATABASE xxx;
Query OK, 1 row affected (0.01 sec)
# 切换到该库
mysql> use xxx
Database changed
# 创建名为person的表
mysql> CREATE TABLE person (id int(10),name char(20),phone int(12));
Query OK, 0 rows affected (0.06 sec)
# 写入示例数据1
mysql> INSERT INTO person VALUES(01,'Tom',110110110);
Query OK, 1 row affected (0.02 sec)
# 写入示例数据2
mysql> INSERT INTO person VALUES(02,'Jack',119119119);
Query OK, 1 row affected (0.02 sec)
# 写入示例数据3
mysql> INSERT INTO person VALUES(03,'Rose',112222119);
Query OK, 1 row affected (0.01 sec)
通过MySQLdb连接并读取数据。
>>> import MySQLdb
# 连接数据库
>>> db = MySQLdb.connect(host="localhost", user="root", passwd="", db="xxx")
# 获取操作游标
>>> cur = db.cursor()
# 使用 execute 方法执行 SQL 查询语句
>>> cur.execute("SELECT * FROM person")
3L
# 输出查询
>>> for row in cur.fetchall():
... print row # 注意缩进
...
(1L, 'Tom', 110110110L)
(2L, 'Jack', 119119119L)
(3L, 'Rose', 112222119L)
# 关闭数据库连接
>>> db.close()
数据预处理
数据概览
Pandas为我们提供了相应的函数。通过这些函数方法,我们可以很方便的概览数据。
import pandas as pd
df = pd.read_csv("test.csv")
# 浏览头部数据
df.head()
# 浏览尾部数据
df.tail()
# 不带参数的head()和tail()方法默认显示5条数据,
# 也可以自定义显示数据条数
df.head(10)
# describe()方法可以对数据集中的数值进行统计,
# 会输出数据计数,最大值,最小值等
df.describe()
count()方法可以用于计算数据集中非空数据的数量:
df.count()
缺失值处理
缺失值是指数据集中的某一些行或列出现数值丢失。造成这种现象的原因,有可能是数据没有采集成功,或者是因为传输、存储出现故障。
1.查找缺失值
查找缺失值,我们依旧可以使用 Pandas 进行处理。Pandas 中,缺失数据一般采用NaN标记NaN 代表 Not a Number。特别地,在时间序列里,时间戳的丢失采用 NaT 标记。
Pandas 用于检测缺失值主要用到两个方法,分别是:isnull() 和 notnull(),故名思意就是「是缺失值」和「不是缺失值」。默认会返回布尔值用于判断。
import pandas as pd
df = pd.read_csv("test.csv")
print(df.head(10).isnull())
print(df.head(10).notnull())
2.删除缺失值所在的列或行
删除缺失值是最简单直接的办法之一。它适用于三种情况:
-
缺失值少,对数据集的影响可以忽略不计。这句话的意思应该很好理解。比如一个数万行的数据集,恰好有某几行缺失了几个特征值。缺失的数据行远远小于全部数据的数量,且删除这几行之后,对原数据集的影响可以忽略。这时候,直接删除缺失值所在的行是最好的。
-
缺失数据量大,已无法挽救。举个例子,一个数据集有1 万行,存在 10 个特征列。其中某一项特征所在的列存在 9000 个空值。这也就表明该列存在的意义已经不大了。所以也需要删除数据。
-
该缺失值无法被填充。这种情况也很常见。就拿我们上面一直在用的 test_file.csv 数据集距离。该数据集实际为洛杉矶人口普查数据。我们可以看到数据集中有一列为 Zip Code,也就是邮编。邮编是客观存在的,也是不能随意更改的。如果某几项邮编缺失,你是无法随意通过一些数值来填充邮编。所以,对应这样的数据行已经没有意义,选择直接删除往往是最好的。
删除缺失值所在的列或行非常简单,使用Pandas提供的 dropna() 方法。dropna() 方法可以将有缺失值的行或列全部移除。当然,你可以使用 axis=0 参数指定行,或 axis=1 参数指定列:
import pandas as pd
df = pd.read_csv("xxx.csv")
print(df.dropna(axis=0))
print(df.dropna(axis=1))
因为可能每一列都有空值,所以删除列要慎用。
3.填充缺失值
除了删除缺失值,对缺失值处理的另外一种方法就是填充缺失值。如果你第一次接触缺失值处理,你可能会认为填充缺失值的处理好于直接删除缺失值。其实并不一定,原因在于填充缺失值会直接改变原有数据集,这可能会影响后续预测分析的结果。所以,使用填充缺失值时一定要更加谨慎。
一般情况下,填充缺失值有三种方法。
-
手动填充。手动填充虽然是笨办法,但往往是效果最好的方法。手动填充非常适合于一种情形,那就是数据可以被人为有效确定。举个例子:上面的洛杉矶人口普查数据表中,第一列为邮编,它用于标记不同的地区。如果邮编有几项数据缺失,那么通过手动筛选再填充邮编就是最适合的方法。原因在于,邮编和其他数据不一样,如果它不存在或不正确,就直接导致这行数据无效,甚至影响到其他数据。手动填充,充分展现了人的灵活性,但同样是一个费时费力的办法。
-
临近填充。 临近填充,故名思意就是采用与缺失值相邻的数据进行填充缺失值的方法。临近填充比较适合于零散的不确定数据。零散,指的是不会连续缺失数十个或上百个数据值。如果连续缺失的值太多,你用临近填充将其变为同一数据值,这对数据集整体的影响可想而知。不确定数据,就是通过视觉观察,无法发现相邻数据之间有什么联系,前后数据时大时小,无法被人为确定或找出规律。
Pandas 提供了用于临近填充的fillna()方法。该方法的使用示例如下:
import pandas as pd
# 被前面的临近值填充
df = pd.read_csv("xxx.csv")
print(df.head(10).fillna(method='pad'))
#被后面的临近值填充
df = pd.read_csv("xxx.csv")
print(df.head(10).fillna(method='bfill'))
fillna()方法在填充时并不会影响原有的数据集。
- 插值填充。插值填充就是采用数学的方法对数据进行插值。举个例子,有一列数据为 [2011, 2012, 2013, 缺失值, 缺失值, 2016, 2017] 。这里,无论你采用向前还是向后填充,其实都不是最好的。你可以发现数据是一个等差数列,缺失值应该分别为[2014, 2015],这也就是一个线性插值的过程。
Pandas 提供了相关插值方法,通过interpolate()方法实现。默认为参数为线性插值,即 method='linear'。举例:
import pandas as pd
df = pd.read_csv("xxx.csv")
print(df.interpolate().head(10))
除此之外,interpolate()方法还有{‘linear’, ‘time’, ‘index’, ‘values’, ‘nearest’, ‘zero’, ‘slinear’, ‘quadratic’, ‘cubic’, ‘barycentric’, ‘krogh’, ‘polynomial’, ‘spline’, ‘piecewise_polynomial’, ‘from_derivatives’, ‘pchip’, ‘akima’}等插值方法可供选择。
- 如果你的数据增长速率越来越快,可以选择 method='quadratic'二次插值。
- 如果数据集呈现出累计分布的样子,推荐选择 method='pchip'。
- 如果需要填补缺省值,以平滑绘图为目标,推荐选择 method='akima'。
- 另外,method='akima',method='barycentric' 和 method='pchip' 需要 Scipy 才能使用。
独热编码
在对数据的预处理过程中,我们会遇到有一些特征列中的样本并不是连续存在的,而是以分类形式存在的情况。例如,某一装置的状态有三种情况,分别为:正常、机械故障、电路故障。如果我们要将这些数据运用到后续的预测分析中,就需要对文字状态进行转换。一般情况下,可以用 0 表示正常,1 代表机械故障,2 代表电路故障。
但是这种映射方式,往往会让学习器认为 2 代表电路故障比 1 代表机械故障更「大」,从而影响模型分析结果,这肯定是不行的。
所以,对于以分类形式存在的特征变量,我们会采用一种叫独热编码(One-Hot Encoding)的方式将其转换成二元特征编码,进一步对特征进行了稀疏处理。独热编码采用位状态寄存器来对个状态进行编码,每个状态都由他独立的寄存器位,并且在任意时候只有一位有效。举例如下:
![](https://img.haomeiwen.com/i4080913/a1f33623b79bb9a0.png)
Pandas提供了更简单的方式来处理以分类形式的变量。可以通过get_dummies()将以分类形式存在的变量转换为独热编码。
import pandas as pd
df = pd.read_csv("one_hot_demo.csv", header=0)
df.head(10)
在此演示文件中,status共有none, electric, engine三类数值。color有red和green两类。
![](https://img.haomeiwen.com/i4080913/3e5c055c55a2d682.png)
oneHot = pd.get_dummies(df[['status', 'color']])
oneHot.head(10)
![](https://img.haomeiwen.com/i4080913/bf9a5216b090f9b6.png)
字符串被进行了独热编码,特征也从原先的2列拓充为5列。所以,独热编码好处多多。
重复值处理
pandas.Dataframe.duplicated()可以用来标识重复数据,数据集中的重复数据行会返回布尔类型True。
上面的独热编码示例数据中存在大量重复数据,就拿它来举例:
import pandas as pd
df = pd.read_csv("one_hot_demo.csv", header=0)
print(pd.DataFrame.duplicated(df).head(10))
![](https://img.haomeiwen.com/i4080913/c001e06898af4356.png)
pandas.DataFrame.drop_duplicates()可以返回一个去重后的数据集。
import pandas as pd
df = pd.read_csv("one_hot_demo.csv", header=0)
print(pd.DataFrame.drop_duplicates(df))
去重后的数据集如下,仅有6行非重复项。
![](https://img.haomeiwen.com/i4080913/3dac31ffb0aaff24.png)
异常值检测
一种简单直观的异常值检测方法,那就是通过箱形图(箱线图)来识别异常数据。箱形图是用来观测数据集分布的一种图形类型。箱形图中,从上到下依次有 6 个数据节点,分别是上界、上四分位、均值、中位数、下四分位、下界。而那些超过上界的值就会被标记为离群点,也就是异常数据。
from matplotlib import pyplot as plt
import pandas as pd
data = pd.read_csv("test_file.csv", header=0)
Total_Population = data["Total_Population"]
plt.boxplot(Total_Population)
plt.show()
![](https://img.haomeiwen.com/i4080913/270cbd2578208fc5.png)
将离群点标记并导出:
from matplotlib import pyplot as plt
import pandas as pd
data = pd.read_csv("test_file.csv", header=0)
Total_Population = data["Total_Population"]
P = plt.boxplot(Total_Population)
outlier = P['fliers'][0].get_ydata()
print(outlier)
![](https://img.haomeiwen.com/i4080913/4adff04036cd958a.png)
DataFrame带有boxplot()方法可以很方便地将所有列数据的箱形图画在一张图中对比:
from matplotlib import pyplot as plt
import pandas as pd
data = pd.read_csv("test_file.csv", header=0)
data.boxplot()
plt.show()
![](https://img.haomeiwen.com/i4080913/a1d0361f358b4fe3.png)