《利用Python进行数据分析》第七章的代码。
# -*- coding:utf-8 -*-
# 《python for data analysis》第七章, 数据规整化 import pandas as pd
import numpy as np
import time start = time.time()
# 1、合并数据集,有merge、join、concat三种方式
# 1.1、数据库风格的dataframe合并(merge & join)
# merge函数将两个dataframe按照键把行连接起来
df1 = pd.DataFrame({
'key': ['a', 'b', 'b', 'c', 'd'],
'data1': [1, 2, 3, 4, 5]
})
df2 = pd.DataFrame({
'key': ['a', 'b', 'c'],
'data2': [6, 7, 8]
})
df = pd.merge(df1, df2, on='key') # on用于显示指定连接的依据,缺省时则依据共有的列名(键)
print(df)
# 当两个dataframe中没有公共键时,可以在merge函数中分别指定左依据(left_on)和右依据(right_on)
df1 = pd.DataFrame({
'key1': ['a', 'b', 'b', 'c', 'd'],
'data1': [1, 2, 3, 4, 5]
})
df2 = pd.DataFrame({
'key2': ['a', 'b', 'c'],
'data2': [6, 7, 8]
})
df = pd.merge(df1, df2, left_on='key1', right_on='key2')
print(df)
# merge缺省是求键的交集(df1中的d没有了)
# 可用how关键字显示指定求交集(inner)还是求并集(outer)还是以左dataframe为准(left)还是以右dataframe为准(right)
df = pd.merge(df1, df2, left_on='key1', right_on='key2', how='outer')
print(df)
# merge还有一个关键字用于对重复的键(非连接依据)进行重命名
df1 = pd.DataFrame({
'key': ['a', 'b', 'b', 'c', 'd'],
'same': range(5),
'data1': [1, 2, 3, 4, 5]
})
df2 = pd.DataFrame({
'key': ['a', 'b', 'c'],
'same': range(3, 0, -1),
'data2': [6, 7, 8]
})
df = pd.merge(df1, df2, on='key', suffixes=('_left', '_right')) # 自动追加
print(df)
print('1-1····························')
# 另外merge的返回值默认会对连接键的数据进行排序,可通过sort关键字显示指定排序与否。对于大样本数据关掉收到更快
# 1.2、索引上的合并
# 若某个dataframe上的连接键为其索引,则可通过merge的right_index和left_index关键字来指定
df1 = pd.DataFrame({
'key': ['a', 'b', 'c'],
'data1': range(3)
})
df2 = pd.DataFrame({'data2': range(3, 0, -1), },
index=['a', 'b', 'd'])
df = pd.merge(df1, df2, left_on='key', right_index=True) # 必要时合并双方的连接键均可用索引
print(df)
# pd的join方法可以更加方便地按照索引进行合并
df1 = pd.DataFrame({'data1': range(3)},
index=['a', 'b', 'c'])
df2 = pd.DataFrame({'data2': range(3, 0, -1)},
index=['a', 'b', 'd'])
df = df1.join(df2)
print(df) # 缺省为左连接,可通过how关键字显示指定
# join方法还可以传入一组dataframe,以列表方式
df3 = pd.DataFrame({'data3': [6, 9, 3]},
index=['a', 'b', 'g'])
df = df1.join([df2, df3], how='outer')
print(df)
print('1-2···········································')
# 1.3 轴向连接,concat方法(concatenate)
# 不同于按键进行拼接,concat之间按照轴的方向进行拼接,更加直观
df1 = pd.Series(range(2), index=['a', 'b'])
df2 = pd.Series(range(3), index=['c', 'd', 'e'])
df = pd.concat([df1, df2])
print(df)
# concat方法默认按axis=0进行拼接,可通过axis关键字进行显式指定
df = pd.concat([df1, df2], axis=1)
print(df) # 按轴1进行拼接,生成dataframe
# 在axis1上拼接时默认是按照并集(how=outer)拼接
# 可以通过join_axes关键字显式指定拼接对象的索引
df = pd.concat([df1, df2], axis=1, join_axes=[['a', 'b', 'g']])
print(df)
# 有时候有在对接对象中区分来源的需求,可用keys参数来实现
df = pd.concat([df1, df2], keys=['df1', 'df2'])
print(df) # 生成的dataframe是层次化索引的(multi-index)
# 在连接轴为横向(axis=1)的情况下,指定keys,则生成的dataframe的index为keys
df = pd.concat([df1, df2], keys=['df1', 'df2'], axis=1) # 若无keys则index为缺省值(0,1,2,……)
print(df)
# concat方法还可以用于拼接dataframe
df1 = pd.DataFrame(np.arange(9).reshape(3, 3), index=['a', 'b', 'c'])
df2 = pd.DataFrame(np.arange(10, 19).reshape(3, 3), index=['x', 'y', 'z'])
df = pd.concat([df1, df2])
print(df)
df = pd.concat([df1, df2], keys=['df1', 'df2'], axis=1)
print(df)
# 很多时候,dataframe的索引只是一个序号,无实际含义,在合并时可能出现重叠的情况,可通过ignore_index关键字在拼接后重新生成index
df1 = pd.DataFrame(np.arange(9).reshape(3, 3), index=range(3))
df2 = pd.DataFrame(np.arange(10, 19).reshape(3, 3), index=range(3))
df = pd.concat([df1, df2], ignore_index=True)
print(df)
print('1-3·····································')
# 1.4、合并重叠数据
# 这一节介绍的是combine_first方法,其功能和小标题有点不符合,按照个人理解,是用一个dataframe(或series)给另一个dataframe(或series)
# 按照行索引与列索引打补丁
# eg.1 series打补丁
a = pd.Series([1, 2, np.nan, 3, np.nan, 4, 5],
index=['a', 'b', 'c', 'd', 'e', 'f', 'g'])
b = pd.Series([6, 7, 8, 9, 10, np.nan, np.nan],
index=['a', 'b', 'c', 'd', 'g', 'e', 'h'])
df = a.combine_first(b)
print(df)
# 上上行代码实现了两个功能
# (1)a中'b'索引对应value缺失而b中有,取过来;'e'索引对应value缺失而b中也没有,所以无法打补丁
# (2)b中有'h'索引是a中没有的,所以带索引、带value直接打补丁过去。
# eg.2 dataframe打补丁
df1 = pd.DataFrame({
'a': [1, 2, 3],
'b': [4, 5, 6],
'c': [np.nan, np.nan, 9]})
df2 = pd.DataFrame(np.arange(-1, -10, -1).reshape(3, 3), columns=['c', 'b', 'd'])
df = df1.combine_first(df2)
print(df)
# df1中的缺失数据为df2中的'c'列数据补充,df2中的'd'列直接全部打到df1中
print('1-4····································')
# 2、重塑与轴向旋转
# 本节介绍对表格型数据进行重新排列,包括stack、unstack、pivot三种函数(pivot_table也是一种函数,创建数据透视表)
# 2.1、重塑层次化索引(stack & unstack)
# ·stack,将数据的列名转换为行名(column2index)
# ·unstack,将数据的行名转换为列名(index2column)
df = pd.DataFrame(np.arange(9).reshape(3, 3),
index=['one', 'two', 'three'],
columns=['a', 'b', 'c'])
print(df)
print(df.stack())
print(df.stack().unstack())
# 上面有2点:1、可以没column但不能没有index,没有column就是series
# 2、stack与unstack搭配使用可以还原,前提是层次化索引的次序没有被破坏
print('2.1····································')
# 2.2、将“长格式”旋转为“宽格式”(pivot方法)
# 之前的stack、unstack是行名(index)与列名(column)之间的转换
# 而pivot是实现了某列的value和列名(column)之间的转换
df1 = pd.DataFrame(np.arange(9).reshape(3, 3), columns=['a', 'b', 'c'], index=['one', 'two', 'three'])
df2 = df1.pivot('a', 'b', 'c')
print(df1)
print(df2)
# pivot方法的三个参数依次表示生成dataframe的index、column、value
# pivot等价于先set_index创建层次化索引再unstack重塑
print('2.2·····································')
# 3、数据转换
# 包括数据的过滤、清理等工作
# 3.1、移除重复数据
# (1)duplicated方法返回一个表示各行是否重复的series(行的每一列,同方为重复)
df = pd.DataFrame({
'a': [1, 1, 1, 1],
'b': [2, 2, 2, 2],
'c': [2, 3, 2, 0],
'd': [3, 3, 3, 0], })
print(df)
print(df.duplicated())
# (2)drop_duplicates方法直接丢弃重复值(保留原索引)
print(df.drop_duplicates())
# drop_duplicates默认保留第一个,可以用take_last=True显示指定保留最后一个
# 3.2、利用函数或映射进行数据转换
# 使用.map()方法可实现元素级的数据转换,map中可以传入字典、函数、lambda函数等
data = pd.DataFrame(np.arange(10).reshape(5, 2), columns=['a', 'b'])
data['a'] = data['a'].map(lambda a: a * a + 1) # dataframe不能直接用.map,对单维度数据类型有效
print(data)
dict = {1: 'a', 2: 'b', 3: 'c'}
data['b'] = data['b'].map(dict) # 字典映射
print(data)
print('3.2··································')
# 3.3、替换值
# replace方法可对Series/DataFrame中的value实现元素级的替换
data = pd.DataFrame(np.arange(10).reshape(5, 2))
data = data.replace(2, 5)
print(data)
# 还可以对一组值进行替换
data = data.replace([0, 1], 'bool')
print(data)
# 还可以传入字典进行对应替换
data = data.replace({3: 'three', 7: 'seven'})
print(data)
print('3.3··································')
# 3.4、重命名轴标签
# (1)对轴标签应用map方法
data = pd.DataFrame(np.arange(9).reshape(3, 3), index=['one', 'two', 'three'], columns=['a', 'b', 'c'])
data.index = data.index.map(str.upper)
print(data)
# (2)使用rename方法
data.rename(index=str.title, columns=str.upper, inplace=True)
print(data)
# rename还可以用字典进行局部替换
data.rename(index={'One': 'ONE'}, inplace=True)
print(data)
print('3.4································')
# 3.5、离散化与面元划分
# 所谓离散化或者面元划分,实际就是把连续的数据分布分成几段进行离散,使用.cut方法进行
np.random.seed(10)
age = np.random.randint(1, 100, 100)
bins = [0, 18, 60, 100] # bins提供划分点,即0~18、18~60、60~100,均为缺省左开右闭区间,可通过right关键字显式指定。
age_bin = pd.cut(age, bins)
print(age)
print(age_bin) # age_bin保存了age中每个元素属于的区间
print(age_bin.codes) # 每个元素对应的区间的标号
print(pd.value_counts(age_bin)) # 每个区间的元素个数
# 上面生成的区间名字可以通过labels关键字进行指定
age_bin = pd.cut(age, bins, labels=['youth', 'adult', 'senior'])
print(age_bin) # 改变
print(age_bin.codes) # 仍为0、1、2
# 如果cut的时候没有传入具体的分割点,而是传入了分成的段数,则cut会自动生成等长面元
age_bin = pd.cut(age, 3) # 等分成3段
print(age_bin)
print(pd.value_counts(age_bin))
# 相应地,有qcut方法进行等数量划分
age_bin = pd.qcut(age, 3) # 等数量划分成3段
print(age_bin)
print(pd.value_counts(age_bin))
# qcut还可以进一步指定每段的样本个数
age_bin = pd.qcut(age, [0, 0.1, 0.3, 1]) # 每段占比10%、20%、70%
print(age_bin)
print(pd.value_counts(age_bin))
print('3.5····································')
# 3.6、检测与过滤异常值
# 这一节不引入新的函数,是一种思想的介绍
# 一种常用的处理离群值的方法:设置饱和值(上下限)
data = pd.DataFrame(np.random.randn(5, 5)) # 5*5的标准高斯分布
print(data)
print(np.where(np.abs(data) > 1)) # 返回达到饱和值的value的index和columns
data[np.abs(data) > 1] = np.sign(data) # 饱和处理,sign()是符号函数
print(data)
print('3.6····································')
# 3.7、排列与随机采样
# 用take函数搭配permutation生成的随机序列可以实现随机不放回采样
data = pd.DataFrame(np.arange(50).reshape(10, 5))
sampler = np.random.permutation(5) # 0~4的不重复随机序列
data2 = data.take(sampler)
print(data)
print(data2)
# 用take函数搭配randint生成的随机序列可以实现随机有放回的采样
sampler = np.random.randint(1, 9, 7)
data2 = data.take(sampler)
print(data2)
print('3.7····································')
# 3.8、计算指标/哑变量
# 类似one-hot处理(独热编码)
df1 = pd.DataFrame({'key': ['a', 'a', 'c', 'b', 'b', 'c', 'b'],
'value': range(7)})
print(df1)
df2 = pd.get_dummies(df1['key']) # 对key进行one-hot编码处理,专业说法:把分类变量转换为哑变量举证(又称指标矩阵)
print(df2)
# 一个很常用的例子:很多时候会对连续变量进行分段实现离散化,离散化之后一般还需要进行哑变量矩阵的构造
age = np.random.randint(1, 100, 10) # 10个年龄值
bins = [0, 18, 60, 100] # bins提供划分点,即0~18、18~60、60~100,均为缺省左开右闭区间,可通过right关键字显式指定。
age_bin = pd.cut(age, bins)
age_dummies = pd.get_dummies(age_bin)
print(age_dummies)
print('3.8·····································')
# 4、字符串操作
# 4.1、字符串对象方法(python原生)
# spilt方法分割字符串
string = 'amy,john,jack,nancy,trump'
for item in string.split(','):
print(item)
# strip方法修剪字符串前后的空格、换行符
string2 = '\n a,b \n'
print(string2)
print(string2.strip())
# python中字符串还可以直接相加来实现拼接
initial_string = ''
for item in string.split(','):
initial_string = initial_string + item + '_'
print(initial_string)
# 用join方式插入连接符
print('_'.join(string.split(',')))
# 字符串中确认是否有某字段
print('amy' in string)
print('jerry' in string)
# 查找字符串中某个字符的下标(index),可使用find和index两种方法
# 区别在于:find找不到则返回-1,index找不到则报错,程序停止(可搭配try使用)
print('')
print(string.find('h'))
print(string.find('jack')) # 只返回第一个字符的值
print(string.find('z'))
print(string.index('h'))
# count方法可以给出字符串中子串出现的次数
print('')
print(string.count('a'))
print(string.count('amy'))
# replace方法用于替换与删除
print('')
print(string.replace(',', '_'))
print(string.replace(',', ''))
print('4.1··························')
# 4.2 pandas中矢量化的字符串函数
# .map可直接用于Series的value,注意dataframe不能这么用
data = pd.Series({
'amy': 'amy@qq.com',
'john': 'john@gmail.com',
'jack': 'jack@aliyun.com',
'jerry': 'jerry@126.com',
'tom': 'np.nan'
})
print(data.map(str.upper))
# 当Series中含有NaN时,.map等方法不能使用。Series有一些可以跳过这些NaN值的方法,可通过.str属性进行访问这些方法(如contains)
data = pd.Series({
'amy': 'amy@qq.com',
'john': 'john@gmail.com',
'jack': 'jack@aliyun.com',
'jerry': 'jerry@126.com',
'tom': np.nan
})
try:
print(data.map(str.upper))
except:
print('there is a NaN in the Series')
print(data.str.contains('.com'))
# .str属性没有.map方法,有.match方法可以应用正则表达式,.split、.strip、.count等均有
print("---------------------that's all----------------------------")
print('total time is %.2fs' % (time.time() - start))