快乐学习
前程无忧、中华英才非你莫属!

原创-史上最强Pandas心法-下半

前言
 
Pandas 下半部分内容,来了。这些示例整合了一下,直接贴上,没怎么分类,因为比较懒
但是内容,肯定是很精华,并配上了中文的注释,并且每行代码,都经过小编的运行和验证。含金量还是非常之高。
示例代码的主要内容有、数据的合并concatmerge 这两个函数,如果你有大量的Excel, 数据库表的复杂链接查询的时候,发现SQL 很慢的时候,可以试试merge函数。
还有分组和汇总,当我们的数据有很多突出的特征,我们就可以进行分类统计和描述
分类统计和描述,就用到了分组函数 gruopby,结合aggregate和聚合函数max,min等就可以了。
还有更为简洁高级点的分组统计功能,那就是透视表pivot_table
还有字符串处理函数,以及时间字段的处理
import numpy as np
import pandas as pd
# Series DataFramepd.concat函数连接在一起
from datetime import datetime
def make_df(cols, ind):
"""Quickly make a DataFrame"""
data = {c: [str(c) + str(i) for i in ind]
for c in cols}
return pd.DataFrame(data, ind)
# example DataFrame
md = make_df('ABC', range(3))
# print(md)
# numpy数组的串联
x = [1, 2, 3]
y = [4, 5, 6]
z = [7, 8, 9]
xyz = np.concatenate([x, y, z])
# print(xyz)
# 设置轴来设定串联的方向
x = [[1, 2],
[3, 4]]
xx = np.concatenate([x, x], axis=1) # 1 x轴方向
# print(xx)
# pd.concat()可以用于SeriesDataFrame对象的简单串联
ser1 = pd.Series(['A', 'B', 'C'], index=[1, 2, 3])
ser2 = pd.Series(['D', 'E', 'F'], index=[4, 5, 6])
ser = pd.concat([ser1, ser2])
# print(ser)
df1 = make_df('AB', [1, 2])
df2 = make_df('AB', [3, 4])
df = pd.concat([df1, df2])
# print('这是concatdf',df)
df3 = make_df('AB', [0, 1])
df4 = make_df('CD', [0, 1])
dfs = pd.concat([df3, df4], axis=1)
# print(dfs)
# 重复索引
x = make_df('AB', [0, 1])
y = make_df('AB', [2, 3])
y.index = x.index # make duplicate indices!
xys = pd.concat([x, y])
# print(xys)
# 给两个DF数据分别在左侧X轴,添加多层索引
xyk = pd.concat([x, y], keys=['x', 'y'])
# print(xyk)
# 串联部分索引不同,出现NaN
df5 = make_df('ABC', [1, 2])
df6 = make_df('BCD', [3, 4])
# 解决办法,使用join 函数内连接,把索引相同部分进行关联
# 其他不关联的索引A,D舍去。可以理解为sql 的内连接
dfn = pd.concat([df5, df6], join='inner')
# print(dfn)
# 可以理解为sql 的左链接,以df5 为基准。
dfc = pd.concat([df5, df6], join_axes=[df5.columns])
# print(dfc)
# 通过append 函数 来合并数据.效果一样
# print('这是appenddf', df1.append(df2))
# 通过append方法不会修改原始对象,
# 而是会使用合并的数据创建一个新对象
# 还是使用concat 比较高效。、
# 1、 高性能合并Merge 合并多个数据源
df_emp = pd.DataFrame({'employee': ['Bob', 'Jake', 'Lisa', 'Sue','ztloo'],
'group': ['Accounting', 'Engineering', 'Engineering', 'HR','code']})
df_date = pd.DataFrame({'employee': ['Lisa', 'Bob', 'Jake', 'Sue','pys'],
'hire_date': [2004, 2008, 2012, 2014,2020]})
# print(df_emp)
# print(df_date)
# 按照第一个参数df_emp 的索引顺序排列的
# merge 只合并两个数据源相同的列,不同的列会舍弃
df3 = pd.merge(df_emp, df_date)
# print(df3)
df4 = pd.DataFrame({'group': ['Accounting', 'Engineering', 'HR'],
'supervisor': ['Carly', 'Guido', 'Steve']})
# 两个df 在去合并一个df,通过group,新增一个关系列。
df34 = pd.merge(df3,df4)
# print(df34)
# 合并的df 数据中有重复索引
df5 = pd.DataFrame({'group': ['Accounting', 'Accounting',
'Engineering', 'Engineering', 'HR', 'HR'],
'skills': ['math', 'spreadsheets', 'coding', 'linux',
'spreadsheets', 'organization']})
# print(df_emp)
# 根据df_emp 中的grop角色进行关联合并。
df15 = pd.merge(df_emp,df5)
# print(df15)
# 通过关键字来合并,显示指定两个df 中都有的字段,用来作为关联。
dfe12 = pd.merge(df_emp, df_date, on='employee')
# print(dfe12)
df3 = pd.DataFrame({'name': ['Bob', 'Jake', 'Lisa', 'Sue','dssf'],
'salary': [70000, 80000, 120000, 90000,1009009]})
# 两个df 中,关联两个索引名称不同的方法。
# 通过left_onright_on 来指定左右关联的索引名称
df13_lr = pd.merge(df_emp, df3, left_on="employee", right_on="name")
# print(df13_lr)
# 去掉重复的列,删掉name 列。
# print(df13_lr.drop('name', axis=1))
df1a = df_emp.set_index('employee')
df2a = df_date.set_index('employee')
# print(df1a)
# print(df2a)
dfaa = pd.merge(df1a, df2a, left_index=True, right_index=True)
# print(dfaa)
# print(df1a.join(df2a))
# print(df1a)
# 因为df1a 的索引set_index 被设置成了employeeso 直接关联name.
dfa3 = pd.merge(df1a, df3, left_index=True, right_on='name')
# print(dfa3)
# 严重的问题
# 两个df 中有不同的列,使用merge 只会合并都存在的列
df6 = pd.DataFrame({'name': ['Peter', 'Paul', 'Mary'],
'food': ['fish', 'beans', 'bread']},
columns=['name', 'food'])
df7 = pd.DataFrame({'name': ['Mary', 'Joseph'],
'drink': ['wine', 'beer']},
columns=['name', 'drink'])
# 为了解决多个df 中不同的列,使用how=outer
# df67 = pd.merge(df6, df7,how='outer')
# print(df67)
# 如果只显示第一个df数据中的所有列,left ,也可以使用right.
df67 = pd.merge(df6, df7,how='left')
# print(df67)
# 合并两个df ,df 中有相同的列名
df8 = pd.DataFrame({'name': ['Bob', 'Jake', 'Lisa', 'Sue'],
'rank': [1, 2, 3, 4]})
df9 = pd.DataFrame({'name': ['Bob', 'Jake', 'Lisa', 'Sue'],
'rank': [3, 1, 4, 2]})
'''
由于输出将有两个冲突的列名,
因此合并功能会自动附加后缀_x_y使输出列唯一。
如果这些默认值不合适,则可以使用suffixes关键字指定自定义后缀:
'''
df89 = pd.merge(df8, df9, on="name")
# print(df89)
df89 = pd.merge(df8, df9, on="name", suffixes=["_L", "_R"])
# print(df89)
##################################################################
pop = pd.read_csv('state-population.csv')
areas = pd.read_csv('state-areas.csv')
abbrevs = pd.read_csv('state-abbrevs.csv')
# print('-----------pop------------')
# print(pop.head())
#
# print('-----------areas------------')
# print(areas.head())
#
# print('-----------abbrevs------------')
# print(abbrevs.head())
# state/region abbreviation 字段相关联
merged = pd.merge(pop, abbrevs, how='outer',
left_on='state/region', right_on='abbreviation')
# drop duplicate info
# 删掉abbreviation
merged = merged.drop('abbreviation', axis=1) # drop duplicate info
# print(merged.head())
#
# 检查数据是否有不匹配有空行
# print(merged.isnull().any())
# 一些population信息为空;让我们找出:
pop_null = merged[merged['population'].isnull()].head()
# print(pop_null)
state_unique = merged.loc[merged['state'].isnull(), 'state/region'].unique()
#unique.返回Series对象的唯一值。
# print(state_unique)
#把合并好的数据中的state/region 等于pr的数据中的state 字段的值修改成Puerto Rico
merged.loc[merged['state/region'] == 'PR', 'state'] = 'Puerto Rico'
#把合并好的数据中的state/region 等于USA的数据中的state 字段的值修改成Puerto Rico
merged.loc[merged['state/region'] == 'USA', 'state'] = 'United States'
#any()一个True,则返回True,非空为False
# print(merged.isnull().any())
# print(merged.head())
# 将结果与面积数据合并。
# 检查我们的结果,我们将希望同时加入state这两个列:
final = pd.merge(merged, areas, on='state', how='left')
# print(final.head())
# print(final.isnull().any())
# area列中为空;我们可以看一下此处忽略了哪些区域:
fun = final['state'][final['area (sq. mi)'].isnull()].unique()
# print(fun)
'''
我们看到,我们areas DataFrame不包括整个美国地区。我们可以插入适当的值(例如,使用所有州面积的总和),但是在这种情况下,我们将删除空值,因为整个美国的人口密度与我们当前的讨论无关:
'''
final.dropna(inplace=True)
# print(final.head())
data2010 = final.query("year == 2010 & ages == 'total'")
# print(data2010.head())
data2010.set_index('state', inplace=True)
density = data2010['population'] / data2010['area (sq. mi)']
density.sort_values(ascending=False, inplace=True)
# print(density.head())
# print(density.tail())
###############2、汇总与分组##########################
import seaborn as sns
planets = sns.load_dataset('planets')
planets.shape
# print(planets.head())
rng = np.random.RandomState(42)
ser = pd.Series(rng.rand(5))
ser.sum()
ser.mean()
df = pd.DataFrame({'A': rng.rand(5),
'B': rng.rand(5)})
# print(df.mean())
# print(df.mean(axis='columns'))
# print(planets.dropna().describe())
'''
Aggregation Description
count() Total number of items
first(), last() First and last item
mean(), median() Mean and median
min(), max() Minimum and maximum
std(), var() Standard deviation and variance
mad() Mean absolute deviation
prod() Product of all items
sum() Sum of all items
These are all methods of DataFrame and Series objects.
'''
# GroupBy: Split, Apply, Combine
df = pd.DataFrame({'key': ['A', 'B', 'C', 'A', 'B', 'C'],
'data': range(6)}, columns=['key', 'data'])
# print(df)
# print(df.groupby('key').sum())
group_op = planets.groupby('method')['orbital_period']
# print(group_op.median())
# 可以直接迭代每个组
# for (method, group) in planets.groupby('method'):
# print("{0:30s} shape={1}".format(method, group.shape))
# gdu = planets.groupby('method')['year'].describe().unstack()
# print(gdu)
# print(df)
'''
aggregate()方法允许更大的灵活性。
它可以采用字符串,函数或其列表,然后一次计算所有聚合
'''
dfk = df.groupby('key').aggregate(['min', np.median, max])
# print(dfk)
rng = np.random.RandomState(0)
df = pd.DataFrame({'key': ['A', 'B', 'C', 'A', 'B', 'C'],
'data1': range(6),
'data2': rng.randint(0, 10, 6)},
columns = ['key', 'data1', 'data2'])
dfagg = df.groupby('key').aggregate({'data1': 'min',
'data2': 'max'})
# print(dfagg)
# 过滤
def filter_func(x):
return x['data2'].std() > 4
# print(df)
# print('-----------------------------')
# print(df.groupby('key').std())
# print('-----------------------------')
# print(df.groupby('key').filter(filter_func))
#
# print('-----------------------------')
# dfr = df.groupby('key').transform(lambda x: x - x.mean())
# print(dfr)
# apply() 方法的应用
'''
apply()方法使您可以将任意函数应用于分组结果。该函数应采用DataFrame,并返回Pandas对象(例如DataFrameSeries)或标量;合并操作将根据返回的输出类型进行调整。
'''
def norm_by_data2(x):
# x is a DataFrame of group values
x['data1'] /= x['data2'].sum()
return x
df_norm = df.groupby('key').apply(norm_by_data2)
# print(df_norm)
# 指定分离键
L = [0, 1, 0, 1, 2, 0]
# print(df.groupby(L).sum())
#字典或series 映射索引到组
df2 = df.set_index('key')
# print(df2)
mapping = {'A': 'vowel', 'B': 'consonant', 'C': 'consonant'}
print('------------------------------')
# print(df2.groupby(mapping).sum())
# 与映射类似,您可以传递将输入索引值并输出组的任何Python函数
# print(df2.groupby(str.lower).mean())
# print(df2.groupby([str.lower, mapping]).mean())
# 分组案例
# decade = 10 * (planets['year'] // 10)
# decade = decade.astype(str) + 's'
# decade.name = 'decade'
# print(planets.groupby(['method', decade])['number'].sum().unstack().fillna(0))
###############3、透视表##########################
# 科学上网就好了。
# 其中包含有关该不幸航程的每位乘客的大量信息,
# 包括性别,年龄,舱位,已付车费等。
titanic = sns.load_dataset('titanic')
# print(titanic.head())
# 让我们按性别查看存活率:
# print(titanic.groupby('sex')[['survived']].mean())
'''
这立即为我们提供了一些见识:总体而言,船上每四位女性中就有三位得以幸存,而只有五分之一的男性得以幸存!
'''
'''
这很有用,但我们可能想更进一步,按照性别和阶级来考察生存率
'''
# print(titanic.head())
# print('--------------------------')
life = titanic.groupby(['sex', 'class'])['survived'].aggregate('mean').unstack()
# print(life)
# print('--------------------------')
'''
透视表
透视表默认处理函数是均值
'''
# tsb= titanic.pivot_table('survived', index='sex', columns='class')
# print(tsb)
'''
这显然比该groupby方法更具可读性,并且产生相同的结果。您可能会期望20世纪初的跨大西洋航行,生存梯度对女性和更高阶层的人都有利。一流的妇女几乎可以确定地生存(嗨,罗斯!),而十分之三的男性中只有一个幸存了(抱歉,杰克!)。
'''
# 多级透视表
'''
例如,我们可能有兴趣将年龄视为第三维。
我们将使用以下pd.cut功能对年龄进行分类:
'''
age = pd.cut(titanic['age'], [0, 18, 80])
tsc = titanic.pivot_table('survived', ['sex', age], 'class')
# print(tsc)
'''
在处理列时,我们也可以应用相同的策略。让我们添加pd.qcut用于自动计算分位数的票价信息:
'''
fare = pd.qcut(titanic['fare'], 2)
dfs = titanic.pivot_table('survived', ['sex', age], [fare, 'class'])
# print(dfs)
'''
额外的数据透视表选项
'''
# survived 统计它sum, fare 统计它均值,以class 分组。索引是性别
tps = titanic.pivot_table(index='sex', columns='class',
aggfunc={'survived':sum, 'fare':'mean'})
# print(tps)
'''
有时,计算每个分组的总数很有用。这可以通过margins关键字完成:
'''
tcm = titanic.pivot_table('survived', index='sex', columns='class', margins=True)
# print(tcm)
'''
在这里,这自动为我们提供了有关按性别分类不可知生存率,
按类别分类不可知性别生存率以及总生存率38%的信息。
可以使用margins_name关键字指定边距标签,默认为"All"
'''
'''
例如:生育水平数据
'''
births = pd.read_csv('births.csv')
# print(births.head())
'''
通过使用数据透视表,我们可以开始更多地了解此数据。
让我们添加一个十年专栏,看看男女出生与十年的关系:
'''
births['decade'] = 10 * (births['year'] // 10)
bgs = births.pivot_table('births', index='decade', columns='gender', aggfunc='sum')
# print(bgs)
'''
字符串操作
'''
monte = pd.Series(['Graham Chapman', 'John Cleese', 'Terry Gilliam',
'Eric Idle', 'Terry Jones', 'Michael Palin'])
# print(monte)
# print(monte.str.lower())
# print(monte.str.len())
# print(monte.str.startswith('T'))
# print(monte.str.split())
'''
字符串函数
len() lower() translate() islower()
ljust() upper() startswith() isupper()
rjust() find() endswith() isnumeric()
center() rfind() isalnum() isdecimal()
zfill() index() isalpha() split()
strip() rindex() isdigit() rsplit()
rstrip() capitalize() isspace() partition()
lstrip() swapcase() istitle() rpartition()
'''
'''
使用正则表达式
'''
'''
Method Description
match() Call re.match() on each element, returning a boolean.
extract() Call re.match() on each element, returning matched groups as strings.
findall() Call re.findall() on each element
replace() Replace occurrences of pattern with some other string
contains() Call re.search() on each element, returning a boolean
count() Count occurrences of pattern
split() Equivalent to str.split(), but accepts regexps
rsplit() Equivalent to str.rsplit(), but accepts regexps
'''
# 通过在每个元素的开头要求一组连续的字符来从每个名字中提取名字:
# print(monte.str.extract('([A-Za-z]+)', expand=False))
'''
例如查找所有以辅音开头和结尾的名称,
并使用字符串开头(^)和字符串结尾($)正则表达式字符
'''
# print(monte.str.findall(r'^[^AEIOU].*[^aeiou]$'))
# print(monte.str[0:3])
'''
字符串还有一些有用的方法
get() 索引每个元素
slice() 切片每个元素
slice_replace() 用传递的值替换每个元素中的切片
cat() 连接字符串
repeat() 重复值
normalize() 返回字符串的Unicode形式
pad() 在字符串的左侧,右侧或两侧添加空格
wrap() 将长字符串拆分成长度小于给定宽度的行
join() 使用传递的分隔符将字符串连接到系列的每个元素中
get_dummies() 提取虚拟变量作为数据框
'''
# print(monte.str.split().str.get(-1))
# 通过分隔符来查询
full_monte = pd.DataFrame({'name': monte,
'info': ['B|C|D', 'B|D', 'A|C',
'B|D', 'B|C', 'B|C|D']})
# print(full_monte)
# get_dummies()例程可让您快速将这些指标变量拆分为DataFrame
# print(full_monte['info'].str.get_dummies('|'))
'''
使用时间序列
'''
'''
pandas是在财务建模的背景下开发的,
因此,正如您可能期望的那样,它包含用于处理日期,时间和时间索引数据的相当广泛的工具集。日期和时间数据有几种形式,我们将在这里讨论:
时间戳记会参考特定时间点(例如,201574日上午7:00)。
时间间隔和时间段指的是特定起点和终点之间的时间长度;
例如,2015年。期间通指的是时间间隔的一种特殊情况,其中每个时间间隔的长度均一且不重叠(例如,24小时长的时间段,包括几天)。
时间增量或持续时间指的是确切的时间长度(例如22.56秒的持续时间)。
'''
# Pandas,提供了一个Timestamp对象,
# 该对象结合了的易用性datetime以及dateutil的高效存储和矢量化接口
date = pd.to_datetime("4th of July, 2015")
# print(date)
# print(date.strftime('%A'))
# print(date + pd.to_timedelta(np.arange(12), 'D'))
index = pd.DatetimeIndex(['2014-07-04', '2014-08-04',
'2015-07-04', '2015-08-04'])
data = pd.Series([0, 1, 2, 3], index=index)
# print(data)
# print(data['2014-07-04':'2015-07-04'])
# print(data['2015'])
'''
熊猫时间序列数据结构
'''
'''
对于时间戳,熊猫提供Timestamp类型。如前所述,它实际上是Pythonnative的替代datetime,但它基于更有效的numpy.datetime64数据类型。关联的索引结构为DatetimeIndex
对于时间段,Pandas提供了Period类型。这基于编码固定频率间隔numpy.datetime64。关联的索引结构为PeriodIndex
对于时间增量或持续时间,Pandas提供Timedelta类型。Timedelta是基于Python的本机datetime.timedelta类型的更有效替代,并且基于numpy.timedelta64。关联的索引结构为TimedeltaIndex
这些日期/时间对象中最基本的是TimestampDatetimeIndex对象。尽管可以直接调用这些类对象,但是使用该pd.to_datetime()函数可以解析多种格式更为常见。将单个日期传递给会pd.to_datetime()产生Timestamp;默认情况下,传递一系列日期会产生DatetimeIndex
'''
dates = pd.to_datetime([datetime(2015, 7, 3), '4th of July, 2015',
'2015-Jul-6', '07-07-2015', '20150708'])
# print(dates)
'''
DatetimeIndex可以通过添加频率代码PeriodIndexto_period()功能将任意值转换为;在这里,我们将'D'用来指示每日的频率:
'''
# print(dates.to_period('D'))
# print(dates - dates[0])
# 常规序列
# print(pd.date_range('2015-07-03', '2015-07-10'))
# print(pd.date_range('2015-07-03', periods=8)) # 生成8个日期
# 生成带小时的8个日期
# print(pd.date_range('2015-07-03', periods=8, freq='H'))
# 生成8个年-
# print(pd.period_range('2015-07', periods=8, freq='M'))
# 生成8个时分秒的日期
# print(pd.timedelta_range(0, periods=10, freq='H'))
'''
频率和偏移量
这些熊猫时间序列工具的基础是频率或日期偏移量的概念。正如我们在上面看到的D(天)和H(小时)代码一样,我们可以使用这些代码来指定任何所需的频率间隔。下表总结了可用的主要代码:
'''
'''
Code Description Code Description
D Calendar day B Business day
W Weekly
M Month end BM Business month end
Q Quarter end BQ Business quarter end
A Year end BA Business year end
H Hours BH Business hours
T Minutes
S Seconds
L Milliseonds
U Microseconds
N nanoseconds
代码 描述 代码 描述
MS 月开始 BMS 营业月开始
QS 季度开始 BQS 业务季度开始
AS 年开始 BAS 营业年度开始
'''
# 生成9个日期,每一个日期相隔两小时30分钟
# print(pd.timedelta_range(0, periods=9, freq="2H30T"))
# 生成一个工作日偏移量
# from pandas.tseries.offsets import BDay
# print(pd.date_range('2015-07-01', periods=5, freq=BDay()))
打赏
赞(0) 打赏
未经允许不得转载:同乐学堂 » 原创-史上最强Pandas心法-下半

特别的技术,给特别的你!

联系QQ:1071235258QQ群:710045715

觉得文章有用就打赏一下文章作者

非常感谢你的打赏,我们将继续给力更多优质内容,让我们一起创建更加美好的网络世界!

支付宝扫一扫打赏

微信扫一扫打赏

error: Sorry,暂时内容不可复制!