数据清洗


学习笔记,参考原作者


数据清洗是数据分析的第一步, 经常需要花费大量的时间来清洗数据或者转换格式。


一、数据预处理

1. 部署环境,导入分析包和数据

import pandas as pd
import numpy as np
DataDF=pd.read_csv('C:/Users/jzgao/Desktop/ecommerce-data/data.csv',encoding = "ISO-8859-1",dtype = str)

# dtype = str,最好读取的时候都以字符串的形式读入,不然可能会使数据失真
# 比如一个0010008的编号可能会读取成10008
# encoding = "ISO-8859-1" -- 用什么解码,一般会默认系统的编码,如果是中文就用 "utf-8"

2. 尝试去理解这份数据集

我们可以通过对数据集提问来判断这份数据能不能满足解答我们的问题,数据是否干净需不需要进一步处理,问题包括但不限于:

数据集多少数据?
包含了什么字段?字段格式是什么?
字段分别代表什么意义
字段之间的关系是什么?可以用做什么分析?或者说能否满足了对分析的要求?
有没有缺失值;如果有的话,缺失值多不多?
现有数据里面有没有脏数据?尤其需要注意人工输入的数据,经常会出现名称写错,多输入空格等等的情况

3. 下面我们就结合代码来看一下数据

# 1.从宏观一点的角度去看数据:查看dataframe的信息
DataDF.info()

![DataDF.info()](https://pic4.zhimg.com/80/v2-1e545431538653299eba0e3c716caa0f_hd.jpg)
```python # 2.检查缺失数据 # 如果你要检查每列缺失数据的数量,使用下列代码是最快的方法。 # 可以让你更好地了解哪些列缺失的数据更多,从而确定怎么进行下一步的数据清洗和分析操作。 DataDF.isnull().sum().sort_values(ascending=False) ```
![检查缺失数据](https://pic3.zhimg.com/80/v2-f92c16a31bfc8156e69dc24ab3e04b5a_hd.jpg)
```python # 3.抽出一部分数据来,人工直观地理解数据的意义,尽可能地发现一些问题 DataDF.head() ```
![抽出一部分数据来](https://pic4.zhimg.com/80/v2-75282587dc4e5139f870d064713e3d9f_hd.jpg)
可以看到:

1)InvoiceDate 的时间出现具体时分,可以删去
2)Description 大概率是人工填写的数据,一般都会有比较多格式问题。
猜测会存在有标点符号掺杂/大小写不一致等问题,所以进一步这些人工填写数据的去重项拎出来研究一下

# 查看这个商品名称的去重项
DataDF['Description'].unique()

![查看去重项](https://pic2.zhimg.com/80/v2-97354e2574bc482209057091b4376bf5_hd.jpg)
```python # 设置输出全部的内容 # threshold就是设置超过了多少条,就会呈现省略 #(比如threshold=10的意思是超过10条就会省略) np.set_printoptions(threshold=np.inf) DataDF['Description'].unique() ```
![全部内容](https://pic4.zhimg.com/80/v2-2c0a927c6d6709fb4549708df0845be7_hd.jpg)
发现有很多空格的问题

根据第一步数据预处理后,整理一下该数据集有下列问题需要处理:

1)调整数据类型:由于一开始用到了 str 来导入,打算后期再更换格式,需要调整数据类型。

2)修改列名:该数据的名称不易于理解,需要改列名

3)选择部分子集:因为有部分列在数据分析中不需要用到

4)可能存在逻辑问题需要筛选:比如 Unit Price 为负

5)格式一致化:Description 可能会存在有标点符号掺杂/大小写不一致/空格重复出现等问题

6)消灭空值:CustomerID、Description、Country 和 UnitPrice 都出现了 NaN 值,需要去掉


**二、调整数据类型**
DataDF.dtypes

![数据类型调整前](https://pic1.zhimg.com/80/v2-3528fb2a4eadd539198913bfc9147e9c_hd.jpg)
```python # 字符串转换为数值(整型) DataDF['Quantity'] = DataDF['Quantity'].astype('int') # 字符串转换为数值(浮点型) DataDF['UnitPrice'] = DataDF['UnitPrice'].astype('float')

DataDF.dtypes

<br>
![数据类型调整后](https://pic2.zhimg.com/80/v2-5ad6c37d0fd41c73669e9ef53cdd48b1_hd.jpg)
<br>




**三、修改列名**

```python
DataDF.head(10)

![列名修改前](https://pic3.zhimg.com/80/v2-47ef427e9fe02e05bab4d71751872816_hd.jpg)
```python # 建立字典字典:旧列名和新列名对应关系 colNameDict = {'InvolceDate':'SaleDate','StockCode':'StockNo'} # !! ⚠️一定要旧列名放在冒号前 # 每组对应关系以[逗号]隔开 DataDF.rename(columns = colNameDict,inplace=True)

DataDF.head(10)

<br>
![列名修改后](https://pic2.zhimg.com/80/v2-cacb109690ff0eb5dc6f617688c4b8b9_hd.jpg)
<br>




**四、选择部分子集**

```python
DataDF

![数据集](https://pic2.zhimg.com/80/v2-96e7e990e1558bcd00a74b74b055dec9_hd.jpg)
```python #选择子集,选择其中一列 subDataDF1=DataDF["InvoiceDate"]

subDataDF1

<br>
![一列子集](https://pic4.zhimg.com/80/v2-0934de9fb5b296706b7f4bcc05bdeea7_hd.jpg)
<br>
```python
#选择子集,选择其中两列
subDataDF2=DataDF[["InvoiceDate","UnitPrice"]]

subDataDF2

![两列子集](https://pic4.zhimg.com/80/v2-b278ff19a5857645d6d75071ba904057_hd.jpg)

利用切片筛选数据功能 df.loc

loc 这个代码有点像 Excel 里面的鼠标左键,可以随意拉动你需要的数据进行切片。

以逗号作为隔开的界限,左边为 index,右边为 column

subDataDF3=DataDF.loc[:,"InvoiceDate"]
subDataDF3
#单一个冒号意味着不作限制的全选

![subDataDF3](https://pic1.zhimg.com/80/v2-ad7318105f9819980b745294977e76fc_hd.jpg)
```python subDataDF4=DataDF.loc[0:9,:] subDataDF4 ```
![subDataDF4](https://pic2.zhimg.com/80/v2-8722bc9b92101f6d47c9f1296906e879_hd.jpg)
```python subDataDF5=DataDF.loc[1:8,"StockNo":"CustomerID"] subDataDF5 ```
![subDataDF5](https://pic4.zhimg.com/80/v2-dd5a50421201e3163a8dfcb7c327609b_hd.jpg)

五、逻辑问题需要筛选

还是 Dataframe.loc 这个函数的知识点。

由于 loc 还可以判断条件是否为 True

DataDF.loc[:,'UnitPrice']>0

![UnitPrice>0](https://pic3.zhimg.com/80/v2-77f3843482b8cc62918c5b183126e6fe_hd.jpg)
一般来说价格不能为负,所以从逻辑上来说如果价格是小于 0 的数据应该予以筛出
#删除异常值:通过条件判断筛选出数据
#查询条件
querySer=DataDF.loc[:,'Quantity']>0
#应用查询条件
print('删除异常值前:',DataDF.shape)
DataDF=DataDF.loc[querySer,:]
print('删除异常值后:',DataDF.shape)

![删除异常值后](https://pic3.zhimg.com/80/v2-1e0b67efb65821fae3e43160a304c706_hd.jpg)

六、格式一致化

1. 大小写/去除空格

将数据中 Descrption 列中所有内容改成大写:

DataDF['Description']= DataDF['Description'].str.upper()

DataDF.head()

类似的代码还有 字符串修改方法:

str().
upper()
lower()
title() 
lstrip()
strip()
# str.strip()把字符串头和尾的空格,以及位于头尾的 \n \t 之类给删掉。
DataDF['Description']= DataDF['Description'].str.strip()

strip()使用方法

2. 去除字符串符号 去乱码

3. 空格分割

#定义函数:分割InvoiceDate,获取InvoiceDate
#输入:timeColSer InvoiceDate这一列,是个Series数据类型
#输出:分割后的时间,返回也是个Series数据类型

def splitSaletime(timeColSer):
    timeList=[]
    for value in timeColSer:
        #例如2018/01/01 12:50,分割后为:2018-01-01
        dateStr=value.split(' ')[0]
        timeList.append(dateStr)
#将列表转行为一维数据Series类型
    timeSer=pd.Series(timeList)
    return timeSer

最后再赋值回去

DataDF.loc[:,'InvoiceDate']=splitSaletime(DataDF.loc[:,'InvoiceDate'])

七、处理缺失值

python 缺失值有 3 种:

1)Python 内置的 None

2)在 pandas 中,将缺失值表示为 NA,表示不可用 not available。

3)对于数值数据,pandas 使用浮点值 NaN(Not a Number)表示缺失数据。 后面出来数据,如果遇到错误:说什么 float 错误,那就是有缺失值,需要处理掉

那 None 和 NaN 有什么区别呢:

None 是 Python 的一种数据类型,

NaN 是浮点类型

两个都用作空值

print(type(None))
print(type(NaN))

None/NaN

1. 去除缺失值

# 再一次提醒检查缺失数据

DataDF.isnull().sum().sort_values(ascending=False)

去除缺失值的知识点:

DataFrame.dropna

DataFrame.dropna(axis=0, how='any', thresh=None, subset=None, inplace=False)

# 默认(axis=0)是逢空值剔除整行,设置关键字参数axis=1表示逢空值去掉整列
# 'any'如果一行(或一列)里任何一个数据有任何出现Nan就去掉整行,
# 'all'一行(或列)每一个数据都是Nan才去掉这整行

DataDF.dropna(how='any')
DataDF.dropna(how='all')

# 更精细的thresh参数,它表示留下此行(或列)时,要求有多少[非缺失值]
DataDF.dropna(thresh = 6 )

2. 填充缺失内容:某些缺失值可以进行填充,方法有以下四种:

  1. 以业务知识或经验推测(默认值)填充缺失值

  2. 以同一指标的计算结果(均值、中位数、众数等)填充缺失值

  3. 用相邻值填充缺失值

  4. 以不同指标的计算结果填充缺失值

去除缺失值的知识点:

DataFrame.fillna

1) 用默认值填充- df.fillna (' ')

我们应该去掉那些不友好的 NaN 值。但是,我们应该用什么值替换呢?这个时候可能要结合你对这个数据集的理解,看填充什么数据才是比较合适,以下是一下常用的方法。

在这个数据集中,我们大致判断 CustomerID 如果是不太重要的,就我们可以用使用 "" 空字符串或其他默认值。

DataDF.Country= DataDF.Country.fillna('Not Given')

2) 以同一指标的计算结果(均值、中位数、众数等)填充缺失值

平均值- df.fillna (df.mean ())

使用数字类型的数据有可能可以通过这样的方法来去减少错误。

比如,这个案例里面的价格。如果用 0 或者 "Not Given" 等来去填充都不太合适,但这个大概的价格是可以根据其他数据估算出来的。

DataDF.UnitPrice = DataDF.UnitPrice.fillna(DataDF.UnitPrice.mean())

3)除此,还有一种常见的方法,就是用相邻的值进行填充,

这在时间序列分析中相当常见,用前面相邻的值向后填充,也可以用后面相邻的值向前填充。

print(DataDF)
print(DataDF.UnitPrice.fillna(method='ffill')) # 前向后填充
print(DataDF.UnitPrice.fillna(method='bfill')) # 后向前填充

4) 以不同指标的计算结果填充缺失值

关于这种方法年龄字段缺失,但是有屏蔽后六位的身份证号可以推算具体的年龄是多少。


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM