文章数据
收藏(次)

【python数据分析】第七章 数据清洗

加关注
import numpy as np
import pandas as pd

type(np.nan) # float 类型, not a number,
--------------------------------------------------------------------------------
s = pd.Series([1,2,np.nan,4])
s.isnull() # 返回 bool 数组, F F T F
--------------------------------------------------------------------------------
s.count() # 3
s.mean() # 2.3333333333333335 7/3 空值不包括在里面
--------------------------------------------------------------------------------
缺失数据处理的函数:
dropna 删除空数据
fillna 指定值填充,或者插值方法(ffill 使用前面的数据填充, bfill 后面的值填充)
isnull 判断
notnull 判断

s.dropna() 返回没有空值的新Series, 原数据不变
================================================================================
滤除缺失数据:
--------------------------------------------------------------------------------
过滤掉缺失数据的办法有很多种。你可以通过pandas.isnull或布尔索引的手工方法,
但dropna可能会更实用一些。对于一个Series,dropna返回一个仅含非空数据和索引值的Series
s.dropna()
s[s.notnull()]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
而对于DataFrame对象,事情就有点复杂了。你可能希望丢弃全NA或含有NA的行或列。
dropna默认丢弃任何含有缺失值的行
frame = pd.DataFrame([[1., 6.5, 3.], [1., np.nan, np.nan],[np.nan, np.nan, np.nan], [np.nan, 6.5, 3.]])
frame 输出:
0 1 2
0 1.0 6.5 3.0
1 1.0 NaN NaN
2 NaN NaN NaN
3 NaN 6.5 3.0

clean = frame.dropna() # 一行中如果有 na 就删除这个行
clean 输出:
0 1 2
0 1.0 6.5 3.0


frame.dropna(how='all') #全部为空才删这行, how : {'any', 'all'}, default 'any'

df.dropna(thresh=2) #当某一行的数据【非空】的数量必须达到指定的才能保留,否则删除
--------------------------------------------------------------------------------
默认是把行作为研究对象,删除整行为空删除,如果把列作为研究对象,只要 axis = 1 就是对列操作
frame.dropna(how='all',axis=1)
================================================================================
填充缺失数据:
fillna方法是最主要的函数
--------------------------------------------------------------------------------
额外删除指定的列: frame.drop(axis=1, columns=['c5'], inplace=True)
--------------------------------------------------------------------------------
frame.fillna(0) 空值全部使用 0 填充
frame.fillna({0:0, 1:1, 2:2, 3:3}) 第一列填充0,第二列填充2... 0 1,2,3就是 columns的名称
frame.fillna({0:0, 1:1, 2:2, 3:3}, inplace=True) 直接修改原数据,

frame.fillna(method='ffill') 本列前一行非空数据填充
frame.fillna(method='bfill', limit=1) 本列后一行非空数据填充, limit=1 如果连续两个空,只能填一个

fillna 方法的定义:
frame.fillna(
value=None,
method=None,
axis=None,
inplace=False,
limit=None,
downcast=None,
) -> Union[ForwardRef('DataFrame'), NoneType]
================================================================================
数据转换:
--------------------------------------------------------------------------------
重复数据
frame = pd.DataFrame(
{'k1': ['a', 'b', 'a', 'b', 'a', 'b', 'b'],
'k2': [1,1,2,3,3,4,4]}
)
frame 输出:
k1 k2
0 a 1
1 b 1
2 a 2
3 b 3
4 a 3
5 b 4
6 b 4

frame.duplicated() # 本行是否和前面的行完全相同,返回一维 bool 数组
data.drop_duplicates() # 删除重复的行

frame['k3'] = range(7) # 新加一列,此时没有重复的行了

frame.drop_duplicates('k1') # 指定列,这样指定的列重复就行了
frame.drop_duplicates(['k1','k2']) # 这两列完全相同,删
frame.drop_duplicates(['k1','k2'], keep='last') # 有复制的,那么是保留最前的,还是最后面的
keep 的取值: keep : {'first', 'last', False}, default 'first' False 是重复的全部不保留
--------------------------------------------------------------------------------
利用函数或映射进行数据转换
--------------------------------------------------------------------------------
frame = pd.DataFrame({
'code':['021','0755', '021', '010', '010'],
'num':['1234', '3232', '2342', '3242', '32422']
})

city_to_map = {
'021':'上海',
'010':'北京',
'0755':'广东',
'0571':'浙江'
}

现在想给 frame 添加一列,根据区号添加城市是哪个, 而且现在有 这样的 字典 city_to_map
如何映射呢? Series 对象有 map 方法 接受一个 dict 的对象
frame['code'].map(city_to_map) 输出:
0 上海
1 广东
2 上海
3 北京
4 北京
Name: code, dtype: object

frame['city'] = frame['code'].map(city_to_map) # 这样就OK了
map 的参数一般都是函数,怎么传递一个 dict 也可以,那行,就用 函数作为参数
frame['city2'] = frame['code'].map(lambda x:city_to_map[x])
--------------------------------------------------------------------------------
替换值:
series = pd.Series([1., -999., 2., -999., -1000., 3.])
series.replace(-999, np.nan)
series.replace([-999,-1000], np.nan) # 这两个都换
series.replace([-999,-1000], [np.nan, 0]) # 依次对应替换
series.replace({-999:np.nan, -1000:0}) # 这样更好
--------------------------------------------------------------------------------
重命名轴索引
--------------------------------------------------------------------------------
frame = pd.DataFrame(np.arange(12).reshape((3, 4)),
index=['r1', 'r2', 'r3'],
columns=['c1', 'c2', 'c3', 'c4'])
--------------------------------------------------------------------------------
frame.index.map(lambda x:x.upper()) # 索引大写,返回 index 对象,不改变 frame 数据
frame.columns.map(lambda x:x.upper()) # 如果要修改就要重复复制给

frame.index = frame.index.map(lambda x:x.upper()) # 改变它,重新赋值即可
--------------------------------------------------------------------------------
frame.rename(index=lambda x:x.upper(), columns=str.title)
修改 index 和 columns 这样的表头数据,内容数据没有变化,而且返回一个修改表头后的,frame 的副本
frame 是不变的。

frame.rename(index={'R1':'w1', 'R2':'w2', 'xx':'wx'})
# index={'R1':'w1', 'R2':'w2'} 这种是映射的方式,映射到了就替换, 不改变原数据,返回副本而已

frame.rename(index={'R1':'w1', 'R2':'w2', 'xx':'wx'}, inplace=True)
# inplace=True 不在返回数据的副本,而是直接修改原数据
--------------------------------------------------------------------------------
离散化和面元划分
--------------------------------------------------------------------------------
ages = [20, 22, 25, 27, 21, 23, 37, 31, 61, 45, 41, 32]
bins = [18, 25, 35, 60, 100] # 5个数据形成4个区间 (18, 25] (25, 35] (35, 60] (60, 100]
把 ages 划分到 bins 指定的区间中,
cats = pd.cut(ages, bins)

# 属性
cats.codes # array([0, 0, 0, 1, 0, 0, 2, 1, 3, 2, 2, 1], dtype=int8)
# 一个数组,数组值的原数据的元素在哪个 区间 的区间下标

cats.categories # IntervalIndex([(18, 25], (25, 35], (35, 60], (60, 100]], closed='right', dtype='interval[int64]')
# 当前的区间, closed='right' 表示 右边闭合

pd.value_counts(cats)
输出: 落到每个区间的数据有几个
(18, 25] 5
(35, 60] 3
(25, 35] 3
(60, 100] 1
dtype: int64
--------------------------------------------------------------------------------
pd.cut(ages, [18, 26, 36, 61, 100], right=False) # 重新切割,right=False 前闭后开
--------------------------------------------------------------------------------
group_names = ['小伙子', '成年人', '中年人', '老头']
cats = pd.cut(ages, bins, labels=group_names) # 指定标签
pd.value_counts(cats)
输出:
小伙子 5
中年人 3
成年人 3
老头 1
dtype: int64
--------------------------------------------------------------------------------
因为人的年龄可以有明确的区间进行界定,但是有些数据不知道有什么样的区间,这时
可以让他自己确定,只要告诉他需要几个区间,然后他会根据数据的最大最小的值确定区间,
并均匀分布, 均匀是指:区间等分,而不是数据落在区间的个数相同
--------------------------------------------------------------------------------
data = np.random.rand(20)
pd.cut(data, 4, precision=2) # 传递数据 4 不是区间,表明是想分成4个区间,
# precision=2 精确两位小数,意思是4个区间平分,有除法计算区间,精确2位
--------------------------------------------------------------------------------
pd.qcut(....) 这个方法和 cut 类似。
当传入 4 个区间时, cut 是 (max-min) / 4 计算区间的长度,每个区间大小一样, 数据落入区间的个数是不确定的。
而 qcut 传入 4, 表示分4个区间时,他分的4个区间保证了每个区间的样本点数一样。这样区间长度就有大有小了
--------------------------------------------------------------------------------



>> 目录 << 


 

分享
收藏
点赞人
举报
文章标签
评论列表

推荐

暂无推荐