文章目录
数据分析—pandas知识点补充
以下用到的文件链接:https://pan.baidu.com/s/1MVNNZRXu110-Y81boR_1yg |
---|
提取码:tpma |
一、数据类型转换及描述统计
1.数据类型转换
用pandas
读取数据很简单,不多说。一般读取数据后用df.dtypes
观察数据类型,将有些数据类型不匹配的进行转换,比如一般日期类的需要转换,有些数值带有“万”的需要转换,比如“8万”,这个读取出来一般是object
类型,需要转换。下面举一个读取二手车相关信息的应用例子:
import pandas as pd
import numpy as np
sec_cars = pd.read_table(r'E:\\BaiduNetdiskDownload\\从0开始数据分析 资料\\第5章 Python数据处理工具--Pandas\\sec_cars.csv', sep=',')
print(sec_cars.head())
print(sec_cars.dtypes)
结果:
观察数据,发现,Boarding_time字段是日期而不是字符串,New_price字段是数值类,不是字符串.所以,这里将其进行类型转换:
# 将Boarding_time改为日期类型
sec_cars.Boarding_time = pd.to_datetime(sec_cars.Boarding_time, format='%Y年%m月') # 年是大Y,月是小m, 日是小d 另外sec_cars.Boarding_time等价于sec_cars['Boarding_time']
# 将New_price字段改为浮点型
sec_cars = sec_cars.replace('暂无', np.nan) # # 将值为'暂无'的改成缺失值NaN 缺失值的替换应该放在数据清洗中,由于这里不做处理类型转换会报错,所以先做处理
sec_cars.New_price = sec_cars.New_price.str[:-1].astype('float') # 将New_price字段的值改为float类型, str[:-1]去掉最后的万字
print(sec_cars.head())
print(sec_cars.dtypes)
结果:
2.描述性统计
接下来,我们可以简要的观察数据描述性统计:
# 观察连续性值的统计值
print(sec_cars.describe())
# 观察离散性值的统计值
print(sec_cars.describe(include='object'))
结果:
以上都是有关数据的统计描述,但并不能清洗的知道数据的形状分布,如数据是否有偏以及是否属于“尖峰厚尾”的特征。接下来,我们可以求出每个数值型的偏度和峰度:
num_variables = sec_cars.columns[sec_cars.dtypes != 'object'][1:] # [1:] 是为了去掉Boarding_time字段
print(num_variables) # 可以把index想象成一个惰性的列表,可以用list转为列表
def skew_kurt(x):
skews = x.skew()
kurts = x.kurt()
return pd.Series([skews, kurts], index=['skew', 'kurt']) # Series([skews, kurts])表示一个Series里面的有两个元素,并且每一个元素是skews,第二个是kurts
a = sec_cars[num_variables].apply(skew_kurt)
print(a)
结果:
对于离散性数据我们可以求其频率频次:
# 统计离散性字段Discharge的各个唯一值的次数以及词频
Freq = sec_cars.Discharge.value_counts() # 这里只能用value_counts()不能用unique(),因为前者返回的是Series,后者是narray
Freq_ratio = Freq/sec_cars.Discharge.count() # 在无缺失值的情况下 sec_cars.Discharge.count()等价于sec_cars.shape[0]
df = pd.DataFrame({
'Freq':Freq,
'Freq_ratio':Freq_ratio
})
print(df.head())
# 把行标签设置为数据框中的列 (用的较多)
df.reset_index(inplace=True) # inplace表示是否在原数据集行操作 index_col于该方法相反
print(df.head())
结果:
二、字符与日期数据的处理
1.日期数据的处理
# 字符串与日期函数的处理
# year 年 month 月 day 日 hour 小时 minute 分钟 second 秒
# date 返回年月日 time 返回时分秒 dayofyear 返回年中第几天 weekofyear 返回年中第几周
# dayofweek 返回周几(0-6) weekday_name返回具体的周几名称 quarter 返回第几季度 days_in_month 返回所在月有多少天
dates = pd.to_datetime(pd.Series(['1989-8-18 13:14:55', '1995-2-16']),
format='%Y-%m-%d %H:%M:%S')
print(dates)
print('返回日期值\n', dates.dt.date)
print('返回日季度\n', dates.dt.quarter)
print('返回所在月有多少天\n', dates.dt.days_in_month)
结果:
2.应用
对下面的数据进行下面几个方面的处理:
- 添加年龄和工龄字段
- 将手机的中间四位隐藏起来
- 根据邮箱信息新增邮箱域名字段
- 基于other字段取出每个人的专业信息
- 去除birthday start_work other 字段
代码实现如下:
# 读取数据
df = pd.read_excel(r'E:\\BaiduNetdiskDownload\\从0开始数据分析 资料\\第5章 Python数据处理工具--Pandas\\data_test03.xlsx')
print(df.head())
print(df.dtypes)
# 将每个字段的类型修改正确
df.birthday = pd.to_datetime(df.birthday, format='%Y/%m/%d') # birthday字段为日期类
df.start_work = pd.to_datetime(df.start_work, format='%Y-%m-%d') # start_work为日期类
df.tel = df.tel.astype('str') # 电话号码为字符串类
print(df.dtypes)
# 添加年龄和工龄字段
df['age'] = pd.datetime.today().year - df.birthday.dt.year
df['work_age'] = pd.datetime.today().year - df.start_work.dt.year
# 将手机的中间四位隐藏起来
df.tel = df.tel.apply(lambda x:x.replace(x[3:7], '****'))
# 根据邮箱信息新增邮箱域名字段
df['email_domain'] = df.email.apply(lambda x:x.split('@')[1])
# 基于other字段取出每个人的专业信息
df['frofession'] = df.other.str.findall('专业:(.*?),')
# 去除birthday start_work other 字段
df.drop(['birthday', 'start_work', 'other'], axis=1, inplace=True)
print(df)
结果:
三、数据子集的获取
数据子集的获取主要有以下三个方法:
(1)df.loc[] [] 左闭右闭 行标签/列标签
(2)df.iloc[] [) 左闭右开 数字索引
(3)df.ix[] 具有上面的两种功能:数字索引、行/列标签 数字是[) 行/列标签是[]
下面用实例来讲解:
df = pd.DataFrame({'name':['张三','李四','王二','丁一','李五'],
'gender':['男','女','女','女','男'],
'age':[23,26,22,25,27]}, columns = ['name','gender','age'])
print(df)
print(df.iloc[1:4, [0,2]])
print(df.loc[1:3, ['name','age']])
print(df.ix[1:3, [0, 2]]) # 如果行标签是数字且与index一样,则会优先当作行/列标签处理,也是就 []
# 将员工的姓名作为行标签
df2 = df.set_index('name')
print(df2)
# 在实际工作中,行的筛选很少是通过写入具体的行索引或行标签,而是对某些列做筛选
# eg:取出所有男性的姓名和年龄
print(df.loc[df.gender == '男', ['name', 'age']])
print(df.ix[df.gender == '男', ['name', 'age']])
# 不能用df.iloc[df.gender == '男', [0, 2]] 因为iloc不允许做条件筛选,必须是严格的数字
结果:
四、透视表功能
透视表的功能主要是对数据进行汇总统计。其函数为:
pd.pivot_table(
data, 数据集
values=None, 指定需要拉入‘数值’框的字段表
index=None, 指定要拉入‘行标签’框的字段列表(分组)
columns=None, 指定要拉入‘列标签’框的字段列表(分组)
aggfunc='mean', 指定统计函数,也可以指定numpy中的统计函数
fill_value=None, 用指定值填充缺失值
margins=False, 是否需要显示行列的统计值,就是每行/列值做汇总
dropna=True, 是否删除整列为缺失值的字段
margins_name='All', 指定行/列的总计名字
)
实际例子:
df = pd.read_csv(r'E:\\BaiduNetdiskDownload\\从0开始数据分析 资料\\第5章 Python数据处理工具--Pandas\\diamonds.csv', sep=',')
print(df.head())
print(pd.pivot_table(df, index='color', columns='table', values='price', aggfunc=np.sum, margins=True, margins_name='总计'))
结果:
五、数据清洗
数据清洗包括数据集是否重复、是否存在缺失值、数据是否具有完整性和一致性、数据是否存在异常值等等。下面将介绍如何识别和处理重复值、缺省值和异常值。
1.删除重复值
重复值的存在会影响数据分析和挖掘结果的准确信,所以要对重复值进行删除。
# 重复值的处理---删除重复值
df = pd.read_excel(r'E:\\BaiduNetdiskDownload\\从0开始数据分析 资料\\第5章 Python数据处理工具--Pandas\\data_test04.xlsx')
print(df.head())
print(any(df.duplicated())) # d 判断是否有重复值 any是只要有一个True就是True,否则是False
df.drop_duplicates(inplace=True) # s 删除重复值
print(df)
结果:
同时,你要删除指定的某些列的重复值也是可以的:
df.drop_duplicates(['install', 'love'], inplace=True)
2.缺失值的处理
当遇到缺失值时,可以用三种方法解决:删除法、替换法和插补法。由于插补法需要用到模型知识,所以在这里不做讲解。
(1)删除法
当观测值(就是缺失值所在的行)占总行的比例小于5%时,可以删除这些缺失行;当所在列的列的缺失值与整列的比值大于85%时,删除整列。
df = pd.read_excel(r'E:\\BaiduNetdiskDownload\\从0开始数据分析 资料\\第5章 Python数据处理工具--Pandas\\data_test05.xlsx')
print(df)
# 1.删除存在缺失值的行
print(df.dropna())
# 2.删除所有值均为缺失值的行
print(df.dropna(how='all')) # how='all'表示全部是缺失值
# 3.删除非缺失值为几个的行
print(df.dropna(thresh=2)) # 筛选出非缺失值个数>=2的行
# 删除列
print(df.drop('age', axis=1))
(2)替换法
# 前向替换
print(df.fillna(method='ffill')) # 如果第一行有缺失,则第一行无法填补
# 后向替换
print(df.fillna(method='bfill')) # 如果最后一行有缺失,则最后一行无法填补
# 常数替换
print(df.fillna(value=0))
# 为不同列替换不同的值
print(df.fillna({'a':0, 'b':df.b.mean()}))
# 统计值替换
print(df.fillna(value={'gender':df.gender.mode()[0], 'age':df.age.mean(), 'income':df.income.median()})) # mode()[0]是众数
3.异常值的处理
异常值是指远离正常值的观测。一般用两种方法:n个标准差法和箱线法。
# 异常值的处理:n个标准差法、箱线法
df = pd.read_table(r'E:\\BaiduNetdiskDownload\\从0开始数据分析 资料\\第5章 Python数据处理工具--Pandas\\sunspots.csv', sep=',')
print(df.head())
# 用n个标准差法判断是否有异常值
xmean = df.counts.mean()
xstd = df.counts.std()
print('n个标准差法判断是否有异常值', any(df.counts > (xmean+2*xstd)), any(df.counts > (xmean-2*xstd)))
# 用箱线法判断是否有异常值
Q3 = df.counts.quantile(q=0.75)
Q1 = df.counts.quantile(q=0.25)
IQR = Q3 - Q1
UL = Q3+1.5*IQR
LL = Q1-1.5*IQR
print('箱线法判断是否有异常值', any(df.counts > UL), any(df.counts < LL)) # 可以看见下线没有异常值
结果:
对样本值进行绘图,观察其是否类似正太分布;若是,则用n个标准差法,否则用箱线法:
import matplotlib.pyplot as plt
# 设置绘图风格
plt.style.use('ggplot')
# 绘制直方图
df.counts.plot(kind='hist', bins=30, normed=True)
# 绘制密度图
df.counts.plot(kind='kde')
# 图形显示
plt.show() # 可以观察到其是右偏,不是正太分布,所以采用箱线法选出异常值
结果:
可以观察到其是右偏,不是正太分布,所以采用箱线法选出异常值:
# 用箱线法选出异常值,并用替换法处理异常值
before = df.counts.describe()
u_replace_value = df.counts[df.counts < UL].max()
print(u_replace_value)
df.counts[df.counts > UL] = u_replace_value
after = df.counts.describe()
print(pd.DataFrame({'before':before, 'after':after}).T)
4.使用函数或映射进行数据转换
df = pd.DataFrame({
'food':['bacon', 'pulled park', 'bacon', 'Pastrami', 'corned beef', 'Bacon', 'Pastrami', 'honey ham', 'nova lox'],
'ounces':[4, 3, 12, 6, 7.5, 8, 3, 5, 6]
})
print(df)
meat_to_animal = {
'bacon':'pig',
'pulled park':'pig',
'pastrami':'cow',
'corned beef':'cow',
'honey ham':'pig',
'nova lox':'salmon'
}
d = df.copy() # 复制
# 根据meat_to_animal添加一列food属于哪类动物
# 1.
df['animal'] = df.food.str.lower().map(meat_to_animal)
print(df)
print('-------')
# 2.
print(d)
d['animal'] = df.food.map(lambda x:meat_to_animal[x.lower()])
print(d)
结果:
补充一个小知识点
df.replace([100, 99], np.nan)
不同于字符串的replace
, 他是将df
的所有的值为100
或者99
的替换为np.nan