【数据清理与特征工程】2-数据清理

2-0 基本概念

import pandas as pd
# 数据下载地址:https://aistudio.baidu.com/aistudio/projectdetail/4359784
df = pd.read_csv("data/pm2.csv")
df.sample(10)
RANKCITY_IDCITY_NAMEExposed days
4247610嘉峪关49
13414798辽阳100
10411691锦州88
3641487阳江47
3136495揭阳45
238261377漯河197
192215204宿迁140
220243252阜阳172
158180544广元119
110122478肇庆89
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 264 entries, 0 to 263
Data columns (total 4 columns):
 #   Column        Non-Null Count  Dtype 
---  ------        --------------  ----- 
 0   RANK          264 non-null    int64 
 1   CITY_ID       264 non-null    int64 
 2   CITY_NAME     264 non-null    object
 3   Exposed days  264 non-null    int64 
dtypes: int64(3), object(1)
memory usage: 8.4+ KB

数据一共264行4列,其中不存在缺失值,

2-1 转化数据类型

注意下面的代码,我们未定义数据类型时默认为object

df = pd.DataFrame([{'col1':'a', 'col2':'1'},
                   {'col1':'b', 'col2':'2'}])
df.dtypes
col1    object
col2    object
dtype: object

现在来改变数据类型,使用astype函数

df['col2-int'] = df['col2'].astype(int)    # ①
df
col1col2col2-int
0a11
1b22

现在我们成功将数据类型设置为int了

df.dtypes
col1        object
col2        object
col2-int     int32
dtype: object

考虑另一种情况,将不同数据类型看作一个数据类型

s = pd.Series(['1', '2', '4.7', 'pandas', '10'])
try:
    s.astype(float)
except ValueError:
    print("ValueError")
ValueError

发现不能将 string 转化为 float

  1. 我们可以忽略错误并转换,得到 object
  2. 或者使用to_numeric函数,强制转换
#
# s.astype(float, errors='ignore')
pd.to_numeric(s, errors='coerce')
0     1.0
1     2.0
2     4.7
3     NaN
4    10.0
dtype: float64

案例

import pandas as pd

df = pd.read_csv('data/sales_types.csv')  # 教材中数据集文件名称为sales_data_types.csv,此处为适应平台要求稍作修改
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 5 entries, 0 to 4
Data columns (total 10 columns):
 #   Column           Non-Null Count  Dtype  
---  ------           --------------  -----  
 0   Customer Number  5 non-null      float64
 1   Customer Name    5 non-null      object 
 2   2016             5 non-null      object 
 3   2017             5 non-null      object 
 4   Percent Growth   5 non-null      object 
 5   Jan Units        5 non-null      object 
 6   Month            5 non-null      int64  
 7   Day              5 non-null      int64  
 8   Year             5 non-null      int64  
 9   Active           5 non-null      object 
dtypes: float64(1), int64(3), object(6)
memory usage: 528.0+ bytes
df
Customer NumberCustomer Name20162017Percent GrowthJan UnitsMonthDayYearActive
010002.0Quest Industries$125,000.00$162500.0030.00%5001102015Y
1552278.0Smith Plumbing$920,000.00$101,2000.0010.00%7006152014Y
223477.0ACME Industrial$50,000.00$62500.0025.00%1253292016Y
324900.0Brekke LTD$350,000.00$490000.004.00%7510272015Y
4651029.0Harbor Co$15,000.00$12750.00-15.00%Closed222014N

分析数据发现有几个 object 对象没有数据类型

Customer Number

应该是ID之类的

df['Customer Number'] = df['Customer Number'].astype(int).astype(str)

Percent Growth

df['Percent Growth'] = df['Percent Growth'].apply(lambda x: float(x.replace("%", "")) / 100)

2016&2017

def cvt_money(val):
    new_value = val.replace("$","").replace(",","")  # ②
    return float(new_value)
df['2016'] = df['2016'].apply(cvt_money)
df['2017'] = df['2017'].apply(cvt_money)

Jan Units

df['Jan Units'] = pd.to_numeric(df['Jan Units'], errors='coerce')

Month-day-year

df['date'] = pd.to_datetime(df[['Month', 'Day', 'Year']])
df['date']
0   2015-01-10
1   2014-06-15
2   2016-03-29
3   2015-10-27
4   2014-02-02
Name: date, dtype: datetime64[ns]

Active

import numpy as np
df['Active'] = np.where(df['Active']=='Y', 1, 0)
df
Customer NumberCustomer Name20162017Percent GrowthJan UnitsMonthDayYearActivedate
010002Quest Industries125000.0162500.00.30500.0110201512015-01-10
1552278Smith Plumbing920000.01012000.00.10700.0615201412014-06-15
223477ACME Industrial50000.062500.00.25125.0329201612016-03-29
324900Brekke LTD350000.0490000.00.0475.01027201512015-10-27
4651029Harbor Co15000.012750.0-0.15NaN22201402014-02-02

练习

movies = pd.read_csv("data/movies.csv", index_col=0)
movies
上映日期片名类型制片国家/地区想看ID导演主演
005月31日哥斯拉2:怪兽之王动作 / 科幻 / 冒险美国40734人25890017迈克尔·道赫蒂维拉·法米加|米莉·波比·布朗|章子怡|莎莉·霍金斯|布莱德利·惠特福德|查尔斯·丹斯|凯尔...
105月31日尺八·一声一世纪录片 / 音乐中国大陆5305人27185648聿馨佐藤康夫|小凑昭尚|蔡鸿文|徐浩鹏|海山|三桥贵风|星梵竹|三冢幸彦|梁文道|陆川|龚琳娜
205月31日卡拉斯:为爱而声纪录片法国2047人27089205汤姆·沃尔夫玛丽亚·卡拉斯|维托里奥·德·西卡|亚里士多德·奥纳西斯|皮埃尔·保罗·帕索里尼|奥马尔·沙...
305月31日托马斯大电影之世界探险记儿童 / 动画英国972人30236340大卫·斯特登蒂娜·德赛|约瑟夫·梅|泰莉莎·加拉赫|凯瑞·莎勒|约翰·哈斯勒|大卫·麦金|金宝·张|彼得...
405月31日花儿与歌声剧情 / 儿童 / 家庭中国大陆136人33393269王蕾魏歆惠|刘晨毅|王润泽|曹一诺|周琳翌|周北辰|曹德祥|郑陈皓淼
...........................
912020年01月25日囧妈剧情 / 喜剧中国大陆6987人30306570徐峥徐峥|王祖蓝|彭昱畅|潘虹
922020年01月25日中国女排剧情中国大陆 / 香港2671人30128916陈可辛巩俐
932020年01月25日大红包喜剧 / 爱情中国大陆20人33457717李克龙包贝尔|李成敏|许君聪|王小利|廖蔚蔚
942020年06月21日六月的秘密剧情 / 悬疑 / 音乐中国大陆 / 美国542人30216731王暘郭富城|苗苗|吴建飞
952020年10月01日黑色假面剧情 / 悬疑中国大陆3957人26986136NaNNaN

96 rows × 8 columns

movies.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 96 entries, 0 to 95
Data columns (total 8 columns):
 #   Column   Non-Null Count  Dtype 
---  ------   --------------  ----- 
 0   上映日期     96 non-null     object
 1   片名       96 non-null     object
 2   类型       96 non-null     object
 3   制片国家/地区  96 non-null     object
 4   想看       96 non-null     object
 5   ID       96 non-null     int64 
 6   导演       93 non-null     object
 7   主演       88 non-null     object
dtypes: int64(1), object(7)
memory usage: 6.8+ KB
movies['想看'] = movies['想看'].apply(lambda x: int(x.replace('人', "")))

练习

movies = pd.read_csv("data/movies.csv", index_col=0)
movies
movies.info()
movies['想看'] = movies['想看'].apply(lambda x: int(x.replace('人', "")))

2-2 处理重复数据

import pandas as pd
d = {'Name':['Newton', 'Galilei', 'Einstein', 'Feynman', 'Newton', 'Maxwell', 'Galilei'],
             'Age':[26, 30, 28, 28, 26, 39, 40],
             'Score':[90, 80, 90, 100, 90, 70, 90]}
df = pd.DataFrame(d,columns=['Name','Age','Score'])
df

基础知识

使用duplicated判断是不是重复的数据

df.duplicated()
df.drop_duplicates()
df.drop_duplicates('Age', keep='last')
df[df.duplicated()].count() / df.count()

项目案例


cpi = pd.read_excel("data/cpi.xls")
cpi.columns = cpi.iloc[1]    
cpi = cpi[2:]    
cpi.drop([11, 12], axis=0, inplace=True)    
cpi['cpi_index'] = ['总体消费', '食品烟酒', '衣着', '居住', '生活服务', '交通通信', '教育娱乐', '医保', '其他']
cpi.drop(['指标'], axis=1, inplace=True)    
cpi.reset_index(drop=True, inplace=True)    
cpi.columns.rename('', inplace=True)    
cpi
dup_ratio = []
for column in cpi.columns:
    col = cpi[column]
    ratio = col[col.duplicated()].count() / col.count()
    dup_ratio.append(round(ratio, 2))
dup_ratio
dr = pd.Series(dup_ratio, index=cpi.columns)
dr

2-3 处理缺失数据

在进行数据挖掘的过程中,理解和清洗数据是最耗费时间的事情。你应该知道数据是如何产生的,哪些特征对业务有影响,只有这样你才能给出最好的数据结果。

在本文中,我们将介绍缺失值的产生原因和缺失值具体的处理方法。

问题1:为什么有缺失值?

现实世界中的数据在大多数情况下都有很多缺失的数据。每个值丢失的原因可能不同。可能有数据丢失或损坏,或者也可能有特定原因。

缺失值产生的原因

数据丢失背后的一些可能原因(产生过程、传输过程、存储过程):

  • 人们不会在数据收集调查中提供有关某些问题的信息。
  • 数据是从各种可用的过去记录中积累的,而不是直接积累的。
  • 数据收集过程中的不准确也会导致数据丢失。
缺失值类型

数据丢失的原因多种多样,但整体可以将它们分为三个主要组:完全随机丢失、随机丢失、不随机丢失。

完全随机缺失 (MCAR)
  • 现象:缺失的数据不遵循任何特定模式,它们只是随机的。
  • 特点:不可能用其余的变量数据来预测这些值,数据的缺失与其余变量无关或独立。
  • 案例:如在数据收集过程中,由于粗心大意丢失了特定样本
随机丢失 (MAR)
  • 现象:数据在特定子集中丢失
  • 特点:可以借助其他功能来预测数据是否存在/不存在,无法自己预测丢失的数据。
  • 案例:如在数据收集过程中,有一些默认选项,可以不做填写
不随机丢失 (NMAR)
  • 现象:确实的数据遵循某种模式,且与数据样本相关。
  • 特点:删除行/列、插补等常用方法将不起作用,缺失的数据与字段相关。
  • 案例:如在数据收集中,采集者根据字段来选择填写某些字段。

问题2:如何分析缺失值?

在Pandas中可以很方便的使用isnull函数来计算是否包含缺失值。

missing_values=train.isnull().sum()

同时也可以使用missingno库来查看缺失值的分布规律:

  • bar:统计每列缺失值的次数
  • matrix:统计缺失值和行数分布规律
  • heatmap:统计列缺失值的相关性
  • dendrogram:统计列确实的组合性

问题3:缺失值需要处理吗?

处理缺失值可以从两个角度考虑:

  • 从数据角度:如果某列的缺失比例大于某一阈值(如大于90%),则可以考虑剔除列;类似的对行的角度也可以这样操作。
  • 从模型角度:如果使用树模型则不用考虑处理,其他模型则需要进行填充或者剔除。

问题4:缺失值如何填充?

使用值填充

使用特殊值填充是最简单的填充方法,主要的优势是速度,可能会带来一定的噪音。

  • 数值列:中位数、中位数、特殊值
  • 类别列:众数、特殊值
最近邻样本填充

如果数据集的行按照日期维度进行组织,则可以考虑使用时序信息完成填充:

  • 向前填充
  • 向后填充
  • 前后填充

如果数据集的行没有时序信息,使用KNN模型可以选择最近邻的样本,然后从样本的维度完成填充。

使用模型预测

数据样本各列之间存在联系,此时可以从列与列的关系完成缺失值填充。

  • 使用回归/分类模型预测列:使用其他列作为特征,待填充列作为标签;
  • 使用自编码器预测缺失值:使用缺失数据作为输入,完整数据作为标签,完成自监督训练。
df = pd.DataFrame({"one":[10, 11, 12], 'two':[np.nan, 21, 22], "three":[30, np.nan, 33]})
df
df = pd.DataFrame({'ColA':[1, np.nan, np.nan, 4, 5, 6, 7], 'ColB':[1, 1, 1, 1, 2, 2, 2]})
df['ColA'].fillna(method='ffill')
df['ColA'].fillna(method='bfill')
%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt

df = pd.read_csv("/home/aistudio/data/data20510/experiment.csv", index_col=0)

fig, ax = plt.subplots()
ax.scatter(df['alpha'], df['belta'])

基础知识

使用duplicated判断是不是重复的数据

df.duplicated()
0    False
1    False
2    False
3    False
4     True
5    False
6    False
dtype: bool
df.drop_duplicates()
NameAgeScore
0Newton2690
1Galilei3080
2Einstein2890
3Feynman28100
5Maxwell3970
6Galilei4090
df.drop_duplicates('Age', keep='last')
NameAgeScore
1Galilei3080
3Feynman28100
4Newton2690
5Maxwell3970
6Galilei4090
df[df.duplicated()].count() / df.count()
Name     0.142857
Age      0.142857
Score    0.142857
dtype: float64

项目案例

cpi = pd.read_excel("data/cpi.xls")
cpi.columns = cpi.iloc[1]    
cpi = cpi[2:]    
cpi.drop([11, 12], axis=0, inplace=True)    
cpi['cpi_index'] = ['总体消费', '食品烟酒', '衣着', '居住', '生活服务', '交通通信', '教育娱乐', '医保', '其他']
cpi.drop(['指标'], axis=1, inplace=True)    
cpi.reset_index(drop=True, inplace=True)    
cpi.columns.rename('', inplace=True)    
cpi
2019年3月2019年2月2019年1月2018年12月2018年11月2018年10月2018年9月2018年8月2018年7月2018年6月2018年5月2018年4月cpi_index
0102.3101.5101.7101.9102.2102.5102.5102.3102.1101.9101.8101.8总体消费
1103.5101.2102102.4102.5102.9103101.9101100.8100.7101.1食品烟酒
2102102101.6101.5101.4101.4101.2101.3101.2101.1101.1101.1衣着
3102.1102.2102.1102.2102.4102.5102.6102.5102.4102.3102.2102.2居住
4101.2101.3101.5101.4101.5101.5101.6101.6101.6101.5101.5101.5生活服务
5100.198.898.799.3101.6103.2102.8102.7103102.4101.8101.1交通通信
6102.4102.4102.9102.3102.5102.5102.2102.6102.3101.8101.9102教育娱乐
7102.7102.8102.7102.5102.6102.6102.7104.3104.6105105.1105.2医保
8101.9102102.3101.6101.5101.3100.7101.2101.2100.9101100.9其他
dup_ratio = []
for column in cpi.columns:
    col = cpi[column]
    ratio = col[col.duplicated()].count() / col.count()
    dup_ratio.append(round(ratio, 2))
dup_ratio
[0.0, 0.11, 0.0, 0.0, 0.22, 0.22, 0.0, 0.0, 0.11, 0.0, 0.11, 0.22, 0.0]
dr = pd.Series(dup_ratio, index=cpi.columns)
dr

2019年3月      0.00
2019年2月      0.11
2019年1月      0.00
2018年12月     0.00
2018年11月     0.22
2018年10月     0.22
2018年9月      0.00
2018年8月      0.00
2018年7月      0.11
2018年6月      0.00
2018年5月      0.11
2018年4月      0.22
cpi_index    0.00
dtype: float64
df = pd.DataFrame({"one":[10, 11, 12], 'two':[np.nan, 21, 22], "three":[30, np.nan, 33]})
df
onetwothree
010NaN30.0
11121.0NaN
21222.033.0
df = pd.DataFrame({'ColA':[1, np.nan, np.nan, 4, 5, 6, 7], 'ColB':[1, 1, 1, 1, 2, 2, 2]})
df['ColA'].fillna(method='ffill')
0    1.0
1    1.0
2    1.0
3    4.0
4    5.0
5    6.0
6    7.0
Name: ColA, dtype: float64

2-4 处理离群数据

%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt

df = pd.read_csv("data//experiment.csv", index_col=0)

fig, ax = plt.subplots()
ax.scatter(df['alpha'], df['belta'])
<matplotlib.collections.PathCollection at 0x17285178520>


png

# https://github.com/mwaskom/seaborn-data.git
# 如果报错,请去官网下载并解压到 seaborn-data文件夹下,一般目录为 C:\Users\23859\seaborn-data
import seaborn as sns
sns.set(style="whitegrid")

tips = sns.load_dataset("tips")    #加载数据集
tips.sample(5)
total_billtipsexsmokerdaytimesize
8415.982.03MaleNoThurLunch2
11924.082.92FemaleNoThurLunch4
16810.591.61FemaleYesSatDinner2
20912.762.23FemaleYesSatDinner2
13816.002.00MaleYesThurLunch2

画出箱线图,发现存在离散值

sns.boxplot(x="day", y="tip", data=tips, palette="Set3")
<AxesSubplot:xlabel='day', ylabel='tip'>


png

ax = sns.boxplot(x="day", y="tip", data=tips)
ax = sns.swarmplot(x="day", y="tip", data=tips, color=".25")   
d:\CS\Apps\anaconda\anaconda3\envs\ai\lib\site-packages\seaborn\categorical.py:1296: UserWarning: 6.5% of the points cannot be placed; you may want to decrease the size of the markers or use stripplot.
  warnings.warn(msg, UserWarning)
d:\CS\Apps\anaconda\anaconda3\envs\ai\lib\site-packages\seaborn\categorical.py:1296: UserWarning: 5.7% of the points cannot be placed; you may want to decrease the size of the markers or use stripplot.
  warnings.warn(msg, UserWarning)

png

项目案例

让我们看看著名的波士顿房价

# 加载数据集
from sklearn.datasets import load_boston
import pandas as pd
boston = load_boston()
x = boston.data
y = boston.target
columns = boston.feature_names

#为了操作方便,将数据集转化为DataFrame类型
boston_df = pd.DataFrame(boston.data)    
boston_df.columns = columns
boston_df.head()
d:\CS\Apps\anaconda\anaconda3\envs\ai\lib\site-packages\sklearn\utils\deprecation.py:87: FutureWarning: Function load_boston is deprecated; `load_boston` is deprecated in 1.0 and will be removed in 1.2.

    The Boston housing prices dataset has an ethical problem. You can refer to
    the documentation of this function for further details.

    The scikit-learn maintainers therefore strongly discourage the use of this
    dataset unless the purpose of the code is to study and educate about
    ethical issues in data science and machine learning.

    In this special case, you can fetch the dataset from the original
    source::

        import pandas as pd
        import numpy as np

        data_url = "http://lib.stat.cmu.edu/datasets/boston"
        raw_df = pd.read_csv(data_url, sep="\s+", skiprows=22, header=None)
        data = np.hstack([raw_df.values[::2, :], raw_df.values[1::2, :2]])
        target = raw_df.values[1::2, 2]

    Alternative datasets include the California housing dataset (i.e.
    :func:`~sklearn.datasets.fetch_california_housing`) and the Ames housing
    dataset. You can load the datasets as follows::

        from sklearn.datasets import fetch_california_housing
        housing = fetch_california_housing()

    for the California housing dataset and::

        from sklearn.datasets import fetch_openml
        housing = fetch_openml(name="house_prices", as_frame=True)

    for the Ames housing dataset.
    
  warnings.warn(msg, category=FutureWarning)
CRIMZNINDUSCHASNOXRMAGEDISRADTAXPTRATIOBLSTAT
00.0063218.02.310.00.5386.57565.24.09001.0296.015.3396.904.98
10.027310.07.070.00.4696.42178.94.96712.0242.017.8396.909.14
20.027290.07.070.00.4697.18561.14.96712.0242.017.8392.834.03
30.032370.02.180.00.4586.99845.86.06223.0222.018.7394.632.94
40.069050.02.180.00.4587.14754.26.06223.0222.018.7396.905.33
boston_df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 506 entries, 0 to 505
Data columns (total 13 columns):
 #   Column   Non-Null Count  Dtype  
---  ------   --------------  -----  
 0   CRIM     506 non-null    float64
 1   ZN       506 non-null    float64
 2   INDUS    506 non-null    float64
 3   CHAS     506 non-null    float64
 4   NOX      506 non-null    float64
 5   RM       506 non-null    float64
 6   AGE      506 non-null    float64
 7   DIS      506 non-null    float64
 8   RAD      506 non-null    float64
 9   TAX      506 non-null    float64
 10  PTRATIO  506 non-null    float64
 11  B        506 non-null    float64
 12  LSTAT    506 non-null    float64
dtypes: float64(13)
memory usage: 51.5 KB
percentlier = boston_df.quantile([0, 0.25, 0.5, 0.75, 1], axis=0)    # ①
IQR = percentlier.iloc[3] - percentlier.iloc[1]
IQR
CRIM         3.595038
ZN          12.500000
INDUS       12.910000
CHAS         0.000000
NOX          0.175000
RM           0.738000
AGE         49.050000
DIS          3.088250
RAD         20.000000
TAX        387.000000
PTRATIO      2.800000
B           20.847500
LSTAT       10.005000
dtype: float64
Q1 = percentlier.iloc[1]    #下四分位
Q3 = percentlier.iloc[3]    #上四分位
(boston_df < (Q1 - 1.5 * IQR)).any()    # ②
CRIM       False
ZN         False
INDUS      False
CHAS       False
NOX        False
RM          True
AGE        False
DIS        False
RAD        False
TAX        False
PTRATIO     True
B           True
LSTAT      False
dtype: bool
(boston_df > (Q3 + 1.5 * IQR)).any()
CRIM        True
ZN          True
INDUS      False
CHAS        True
NOX        False
RM          True
AGE        False
DIS         True
RAD        False
TAX        False
PTRATIO    False
B          False
LSTAT       True
dtype: bool
boston_df_out = boston_df[~((boston_df < (Q1 - 1.5 * IQR)) |(boston_df > (Q3 + 1.5 * IQR))).any(axis=1)]
boston_df_out.shape
(274, 13)
# 计算z值
from scipy import stats    #统计专用模块
import numpy as np
rm = boston_df['RM']
z = np.abs(stats.zscore(rm))    # ③
st = boston_df['RM'].std()    # ④
st
0.7026171434153237
threshold = 3 * st   #阈值,不是“阀值”
print(np.where(z > threshold))    # ⑤
(array([ 97,  98, 162, 163, 166, 180, 186, 195, 203, 204, 224, 225, 226,
       232, 233, 253, 257, 262, 267, 280, 283, 364, 365, 367, 374, 384,
       386, 406, 412, 414], dtype=int64),)
rm_in = rm[(z < threshold)]    # ⑥
rm_in.shape
(476,)
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值