缺失值处理

如何处理nan

  • 判断数据是否为NaN:

    • pd.isnull(df),
    • pd.notnull(df)
  • 处理方式:

    • 存在缺失值nan,并且是np.nan:

      • 1、删除存在缺失值的:dropna(axis='rows')

        • 注:不会修改原数据,需要接受返回值
      • 2、替换缺失值:fillna(value, inplace=True)

        • value:替换成的值
        • inplace:True:会修改原数据,False:不替换修改原数据,生成新的对象
    • 不是缺失值nan,有默认标记的

    什么是NaN?

In [2]: import numpy as np

In [3]: print(np.NaN, type(np.NaN))
nan <class 'float'>
In [1]: import pandas as pd

In [2]: movie_data = pd.read_csv("./Desktop/IMDB-Movie-Data.csv")

In [3]: pd.isnull(movie_data)
Out[3]:
      Rank  Title  Genre  Description  Director  ...  Runtime (Minutes)  Rating  Votes  Revenue (Millions)  Metascore
0    False  False  False        False     False  ...              False   False  False               False      False
1    False  False  False        False     False  ...              False   False  False               False      False
2    False  False  False        False     False  ...              False   False  False               False      False
3    False  False  False        False     False  ...              False   False  False               False      False
4    False  False  False        False     False  ...              False   False  False               False      False
..     ...    ...    ...          ...       ...  ...                ...     ...    ...                 ...        ...
995  False  False  False        False     False  ...              False   False  False                True      False
996  False  False  False        False     False  ...              False   False  False               False      False
997  False  False  False        False     False  ...              False   False  False               False      False
998  False  False  False        False     False  ...              False   False  False                True      False
999  False  False  False        False     False  ...              False   False  False               False      False

[1000 rows x 12 columns]

In [4]: pd.notnull(movie_data)
Out[4]:
     Rank  Title  Genre  Description  Director  ...  Runtime (Minutes)  Rating  Votes  Revenue (Millions)  Metascore
0    True   True   True         True      True  ...               True    True   True                True       True
1    True   True   True         True      True  ...               True    True   True                True       True
2    True   True   True         True      True  ...               True    True   True                True       True
3    True   True   True         True      True  ...               True    True   True                True       True
4    True   True   True         True      True  ...               True    True   True                True       True
..    ...    ...    ...          ...       ...  ...                ...     ...    ...                 ...        ...
995  True   True   True         True      True  ...               True    True   True               False       True
996  True   True   True         True      True  ...               True    True   True                True       True
997  True   True   True         True      True  ...               True    True   True                True       True
998  True   True   True         True      True  ...               True    True   True               False       True
999  True   True   True         True      True  ...               True    True   True                True       True

[1000 rows x 12 columns]

In [7]: import numpy as np

In [8]: np.all(pd.isnull(movie_data))  # 当全部为True时,返回True
Out[8]: False

In [9]: np.any(pd.isnull(movie_data))  # 只要有一个为True时,返回True
Out[9]: True
In [12]: new_data = movie_data.dropna()

In [13]: np.all(pd.isnull(new_data))
Out[13]: False

In [14]: np.any(pd.isnull(new_data))
Out[14]: False

存在缺失值nan,并且是np.nan

  • 1、删除

pandas删除缺失值,使用dropna的前提是,缺失值的类型必须是np.nan

# 不修改原数据
movie.dropna()

# 可以定义新的变量接受或者用原来的变量名
data = movie.dropna()
  • 2、替换缺失值
# 替换存在缺失值的样本的两列
# 替换填充平均值,中位数
# movie['Revenue (Millions)'].fillna(movie['Revenue (Millions)'].mean(), inplace=True)

替换所有缺失值:

for i in movie.columns:  # 遍历所有列数据
    if np.all(pd.notnull(movie[i])) == False:  # 判断某列是否含有空值
        print(i)  # 打印该列名称
        movie[i].fillna(movie[i].mean(), inplace=True)  # 使用该列的平均值对空值进行填充

不是缺失值nan,有默认标记的

数据是这样的:

问号缺失值
问号缺失值

wis = pd.read_csv("https://archive.ics.uci.edu/ml/machine-learning-databases/breast-cancer-wisconsin/breast-cancer-wisconsin.data")

以上数据在读取时,可能会报如下错误:

URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:833)>

解决办法:

# 全局取消证书验证
import ssl
ssl._create_default_https_context = ssl._create_unverified_context

处理思路分析:

  • 1、先替换‘?’为np.nan

    • df.replace(to_replace=, value=)

      • to_replace:替换前的值
      • value:替换后的值
# 把一些其它值标记的缺失值,替换成np.nan
wis = wis.replace(to_replace='?', value=np.nan)
  • 2、在进行缺失值的处理
# 删除
wis = wis.dropna()

数据离散化

为什么要离散化

连续属性离散化的目的是为了简化数据结构,数据离散化技术可以用来减少给定连续属性值的个数。离散化方法经常作为数据挖掘的工具。

什么是数据的离散化

连续属性的离散化就是在连续属性的值域上,将值域划分为若干个离散的区间,最后用不同的符号或整数 值代表落在每个子区间中的属性值。

离散化有很多种方法,这使用一种最简单的方式去操作

  • 原始人的身高数据:165,174,160,180,159,163,192,184
  • 假设按照身高分几个区间段:150~165, 165~180,180~195

这样我们将数据分到了三个区间段,我可以对应的标记为矮、中、高三个类别,最终要处理成一个"哑变量"矩阵

哑变量矩阵
哑变量矩阵

股票的涨跌幅离散化

我们对股票每日的"p_change"进行离散化

读取股票的数据

先读取股票的数据,筛选出p_change数据

data = pd.read_csv("./data/stock_day.csv")
p_change= data['p_change']

将股票涨跌幅数据进行分组

使用的工具:

  • pd.qcut(data, bins):

    • 对数据进行分组将数据分组 一般会与value_counts搭配使用,统计每组的个数
  • series.value_counts():统计分组次数
# 自行分组
qcut = pd.qcut(p_change, 10)
# 计算分到每个组数据个数
qcut.value_counts()

自定义区间分组:

  • pd.cut(data, bins)
# 自己指定分组区间
bins = [-100, -7, -5, -3, 0, 3, 5, 7, 100]
p_counts = pd.cut(p_change, bins)

股票涨跌幅分组数据变成one-hot编码

  • 什么是one-hot编码

把每个类别生成一个布尔列,这些列中只有一列可以为这个样本取值为1.其又被称为热编码。

把下图中左边的表格转化为使用右边形式进行表示:

  • pandas.get_dummies(data, prefix=None)

    • data:array-like, Series, or DataFrame
    • prefix:分组名字
# 得出one-hot编码矩阵
dummies = pd.get_dummies(p_counts, prefix="rise")

输出截图
输出截图

合并

如果你的数据由多张表组成,那么有时候需要将不同的内容合并在一起分析。

pd.concat实现数据合并

  • pd.concat([data1, data2], axis=1)

    • 按照行或列进行合并,axis=0为列索引,axis=1为行索引

比如我们将刚才处理好的one-hot编码与原数据合并

股票哑变量合并
股票哑变量合并

# 按照行索引进行
pd.concat([data, dummies], axis=1)  # 第二个参数一定要加上!!!

pd.merge

  • pd.merge(left, right, how='inner', on=None, left_on=None, right_on=None)

    • 可以指定按照两组数据的共同键值对合并或者左右各自
    • left: A DataFrame object
    • right: Another DataFrame object
    • on: Columns (names) to join on. Must be found in both the left and right DataFrame objects.
    • left_on=None, right_on=None:指定左右键
Merge methodSQL Join NameDescription
leftLEFT OUTER JOINUse keys from left frame only
rightRIGHT OUTER JOINUse keys from right frame only
outerFULL OUTER JOINUse union of keys from both frames
innerINNER JOINUse intersection of keys from both frames

pd.merge合并

left = pd.DataFrame({'key1': ['K0', 'K0', 'K1', 'K2'],
                        'key2': ['K0', 'K1', 'K0', 'K1'],
                        'A': ['A0', 'A1', 'A2', 'A3'],
                        'B': ['B0', 'B1', 'B2', 'B3']})

right = pd.DataFrame({'key1': ['K0', 'K1', 'K1', 'K2'],
                        'key2': ['K0', 'K0', 'K0', 'K0'],
                        'C': ['C0', 'C1', 'C2', 'C3'],
                        'D': ['D0', 'D1', 'D2', 'D3']})

# 默认内连接
result = pd.merge(left, right, on=['key1', 'key2'])

内连接
内连接

  • 左连接
result = pd.merge(left, right, how='left', on=['key1', 'key2'])

左连接
左连接

  • 右连接
result = pd.merge(left, right, how='right', on=['key1', 'key2'])

右连接
右连接

  • 外链接
result = pd.merge(left, right, how='outer', on=['key1', 'key2'])

外链接
外链接

交叉表与透视表

交叉表与透视表什么作用

探究股票的涨跌与星期几有关?

以下图当中表示,week代表星期几,1,0代表这一天股票的涨跌幅是好还是坏,里面的数据代表比例

可以理解为所有时间为星期一等等的数据当中涨跌幅好坏的比例

交叉表透视表作用
交叉表透视表作用
crosstab
crosstab

使用crosstab(交叉表)实现上图

交叉表:交叉表用于计算一列数据对于另外一列数据的分组个数(寻找两个列之间的关系)

  • pd.crosstab(value1, value2)
  • DataFrame.pivot_table([], index=[])

案例分析

数据准备

  • 准备两列数据,星期数据以及涨跌幅是好是坏数据
  • 进行交叉表计算
# 寻找星期几跟股票张得的关系
# 1、先把对应的日期找到星期几
date = pd.to_datetime(data.index).weekday
data['week'] = date

# 2、假如把p_change按照大小去分个类0为界限
data['posi_neg'] = np.where(data['p_change'] > 0, 1, 0)  # 大于0,涨股,盈利;小于0,跌股,亏损。

# 通过交叉表找寻两列数据的关系
count = pd.crosstab(data['week'], data['posi_neg'])

但是我们看到count只是每个星期日子的好坏天数,并没有得到比例,该怎么去做?

  • 对于每个星期一等的总天数求和,运用除法运算求出比例
# 算数运算,先求和
sum = count.sum(axis=1).astype(np.float32)

# 进行相除操作,得出比例
pro = count.div(sum, axis=0)

查看效果

使用plot画出这个比例,使用stacked的柱状图

pro.plot(kind='bar', stacked=True)
plt.show()

分组与聚合

分组与聚合通常是分析数据的一种方式,通常与一些统计函数一起使用,查看数据的分组情况

想一想其实刚才的交叉表与透视表也有分组的功能,所以算是分组的一种形式,只不过他们主要是计算次数或者计算比例!!看其中的效果:

分组效果
分组效果

什么分组与聚合

分组聚合原理
分组聚合原理

分组API

  • DataFrame.groupby(key, as_index=False)

    • key:分组的列数据,可以多个
  • 案例:不同颜色的不同笔的价格数据
col =pd.DataFrame({'color': ['white','red','green','red','green'], 'object': ['pen','pencil','pencil','ashtray','pen'],'price1':[5.56,4.20,1.30,0.56,2.75],'price2':[4.75,4.12,1.60,0.75,3.15]})

  • 进行分组,对颜色分组,price进行聚合
# 分组,求平均值
col.groupby(['color'])['price1'].mean()
col['price1'].groupby(col['color']).mean()

color
green    2.025
red      2.380
white    5.560
Name: price1, dtype: float64

# 分组,数据的结构不变
col.groupby(['color'], as_index=False)['price1'].mean()

color    price1
0    green    2.025
1    red    2.380
2    white    5.560

星巴克零售店铺数据

现在我们有一组关于全球星巴克店铺的统计数据,如果我想知道美国的星巴克数量和中国的哪个多,或者我想知道中国每个省份星巴克的数量的情况,那么应该怎么办?

数据来源:https://www.kaggle.com/starbucks/store-locations/data

数据获取

从文件中读取星巴克店铺数据

# 导入星巴克店的数据
starbucks = pd.read_csv("./data/starbucks/directory.csv")

3.2 进行分组聚合

# 按照国家分组,求出每个国家的星巴克零售店数量
count = starbucks.groupby(['Country']).count()

画图显示结果

count['Brand'].plot(kind='bar', figsize=(20, 8))
plt.show()

假设我们加入省市一起进行分组

# 设置多个索引,set_index()
starbucks.groupby(['Country', 'State/Province']).count()

仔细观察这个结构,与我们前面讲的哪个结构类似??

与前面的MultiIndex结构类似

综合案例

需求

现在我们有一组从2006年到2016年1000部最流行的电影数据,数据来源:https://www.kaggle.com/damianpanek/sunday-eda/data

  • 问题1:我们想知道这些电影数据中评分的平均分,导演的人数等信息,我们应该怎么获取?
  • 问题2:对于这一组电影数据,如果我们想rating,runtime的分布情况,应该如何呈现数据?
  • 问题3:对于这一组电影数据,如果我们希望统计电影分类(genre)的情况,应该如何处理数据?

实现

首先获取导入包,获取数据

%matplotlib inline
import pandas  as pd 
import numpy as np
from matplotlib import pyplot as plt
#文件的路径
path = "./data/IMDB-Movie-Data.csv"
#读取文件
df = pd.read_csv(path)

问题一:

我们想知道这些电影数据中评分的平均分,导演的人数等信息,我们应该怎么获取?

  • 得出评分的平均分

使用mean函数

df["Rating"].mean()
  • 得出导演人数信息

求出唯一值,然后进行形状获取

## 导演的人数
# df["Director"].unique().shape[0]
np.unique(df["Director"]).shape[0]

644

问题二:

对于这一组电影数据,如果我们想Rating,Runtime (Minutes)的分布情况,应该如何呈现数据?

  • 直接呈现,以直方图的形式

选择分数列数据,进行plot

df["Rating"].plot(kind='hist',figsize=(20,8))

  • Rating进行分布展示

进行绘制直方图

plt.figure(figsize=(20,8),dpi=80)
plt.hist(df["Rating"].values,bins=20)
plt.show()

修改刻度的间隔

# 求出最大最小值
max_ = df["Rating"].max()
min_ = df["Rating"].min()

# 生成刻度列表
t1 = np.linspace(min_,max_,num=21)

# [ 1.9    2.255  2.61   2.965  3.32   3.675  4.03   4.385  4.74   5.095  5.45   5.805  6.16   6.515  6.87   7.225  7.58   7.935  8.29   8.645  9.   ]

# 修改刻度
plt.xticks(t1)

# 添加网格
plt.grid()

  • Runtime (Minutes)进行分布展示

进行绘制直方图

plt.figure(figsize=(20,8),dpi=80)
plt.hist(df["Runtime (Minutes)"].values,bins=20)
plt.show()

修改间隔

# 求出最大最小值
max_ = df["Runtime (Minutes)"].max()
min_ = df["Runtime (Minutes)"].min()

# # 生成刻度列表
t1 = np.linspace(min_,max_,num=21)

# 修改刻度
plt.xticks(np.linspace(min_,max_,num=21))

# 添加网格
plt.grid()

问题三:

对于这一组电影数据,如果我们希望统计电影分类(genre)的情况,应该如何处理数据?

  • 思路分析

    • 思路

      • 1、创建一个全为0的dataframe,列索引置为电影的分类,temp_df
      • 2、遍历每一部电影,temp_df中把分类出现的列的值置为1
      • 3、求和
  • 1、创建一个全为0的dataframe,列索引置为电影的分类,temp_df

首先对数据进行分析,会发现一个电影不只属于一个分类:

数据分析
数据分析

而存储的格式是使用“,”作为分隔符链接为字符串。

第一步:取出所有电影的分类:

[i for i in movie_data['Genre']]

对每个电影的分类字符串做分割:

[i.split(",") for i in movie_data['Genre']]

得到所有电影分类(有重复值):

[j for i in movie_data['Genre'] for j in i.split(",")]

得到无重复分类:

all_class = pd.unique([j for i in movie_data['Genre'] for j in i.split(",")])  # 无重复分类
temp_class = [i.split(",") for i in movie_data["Genre"]]

构建矩阵:

data_frame = pd.DataFrame(np.zeros([movie_data.shape[0], all_class.shape[0]]), columns=all_class, index=movie_data["Title"])

矩阵的列值为所有电影分类,行值为所有电影名称,创建之初所有单元格值都为0。

对矩阵进行赋值:

for i in range(movie_data.shape[0]):
    data_frame.iloc[i, data_frame.columns.get_indexer(temp_class[i])] = 1

赋值后的矩阵
赋值后的矩阵

对列值求和:

end_data = data_frame.sum().sort_values(ascending=True)

绘图:

end_data.plot(kind="bar",figsize=(20,8),fontsize=20,colormap="cool")