[TOC]
Pandas 是 Excel 和 SQL 的结合体。Excel 和 SQL 中实现的数据处理功能,Pandas 基本上都能实现。
pandas 能够替换一些非常复杂的嵌套 for 循环,或者 if 的嵌套,能用 pandas 简化,就尽量用 pandas 简化,包括将 pandas 的所有值转换成一个 字典 list
pandas 能够简化大多数的数据处理,能用 pandas,就尽量不要用其他逻辑
很多时候,是需要什么功能,去找 pandas 的功能,而不是学会了再去用,这种思想要转变过来
学习 pandas 一定要有开放的心态,我们不可能把所有的方法都记住的,我们需要具备的能力,是能敏锐的观察到 pandas 会提供一种解决这个问题的方法,然后我们再去快速的找出来;其实学习其他技术也是如此。
Pandas 几乎所有的方法,都是级联的,cascade,在方法处理完后,都会返回一个 pandas 对象
Series 是一个类数组的数据结构,同时带有标签(lable)或者说索引(index)。
新建 Series 对象,有三种常见方法
from pandas import Seriesser1 = Series([1,2,3,4]) # 直接用 list 生成,index 默认从 0 开始ser1 = Series([1,2,3,4], index=[0,1,2,3]) # 指定 index,values 值不支持 关键字参数sdata = {'Ohio': 35000, 'Texas': 71000, 'Oregon': 16000, 'Utah': 5000} # 用字典生成,key 作为 index,value 作为 value,采用此种生成方式,比较耗费内存ser3 = Series(sdata)
Series 查找的时间复杂度为 O(1),可以按照元素在 series 中的位置进行查找,也可以按照标签进行查找
series_demo[0]series["name"]# DataFrame Demofor index, row in pd_data.iterrows():print row[0] # 按照位置查找print row["name"] # 按照标签查找# 对 Series 的 index 重新赋值pd_series.index = ["1","2", "3", "4"]
统计每个 value 出现的次数
pd_demo = pd.Series([1,2,3,4,5,5,2,3])pd_demo.value_counts() # 返回一个 pandas.Series 对象,index为value 集合,value 为每一个index的频数,按照降序排列
# 计算分位数pd_series.quantile(0.5) # 将 pd_series 中的数先排序,然后获取 0.9 分位的数
# unique 方法,获取去重后的值,其功能等价于 set()pd_series = pd_series.unique()
DataFrame 的算数运算/函数运算等各种运算,其都是按照 index 进行匹配,我们在计算的时候,不能忽略 index 这个潜藏的因素
一个 DataFrame 对象就是一张数据库中的表。
DataFrame 构造方式:
第一种:传入一个 元素为 dict 的 sequence
第二种:传入一个元素为 tuple 的 sequence,并指定 column
第三种:传入一个 dict
第四种:传入一个 DataFrame (浅拷贝)
指定 index,column,构建一个空的 DataFrame
注意,对于没有指定 index 的 DataFrame,其 Index 为 [0, 1, 2, 3, ……]
import pandas as pd# 第一种:dict in listmy_list = [{"name1":"wanshuo","age":"11"}, {"name1":"wangyanan","age":"13"}]pd_data = pd.DataFrame(my_list) # 传入一个 list# 第二种:tuple in list + columnmy_list = [("wansho", 11), ("wandongxin", 12)]pd_data = pd.DataFrame(my_list, columns=["name", "age"])# 第三种:dictdata = {'name' : ['万朔', '王雅楠'], 'age' : [12,13]}pd_data = pd.DataFrame(data)# 第四种:传入一个 DataFramepd.DataFrame(dataframe)# 构建空的 DataFrame,但是有 columns,有 indexpd_result = pd.DataFrame(index=["index1", "index2"], columns=["timelist", "type", "name", "refer"]) # pd_result 仍然是空的 pd_result.empty
empty 适用于 Pandas 中的任何对象,包括 DataFrame, Series
# empty 判空pandas_data = pd.DataFrame()if pandas_data.empty:continue
# replace 替换方法pd_data.replace([100, 101], 1) # 把所有的 100 和 101 替换成 1pd_data.replace([100, 101], [1, 2]) # 把 100 替换成 1,101 替换成 2
pd_hour_statistics.loc[run_id_period, cid] += 1 # loc 的第一个值为 index,第二个值就是 column 的名字
# 遍历 DataFrame 每行for index, row in pandas_data_deployed.iterrows():deploy_plat = row['deploy_plat'] # 直接传入source = row['source']module = row['module']# 上面的代码可以用这个代替data['rows'] = pandas_data.T.to_dict().values()
cancat 分为横向连接,纵向连接
# 纵向的连接,相当于给表中加入更多的元组pd_deployed = [pd_863, pd_noah, pd_beehive, pd_dfp]pd_deployed = pd.concat(pd_deployed).fillna("") # 连接并且填充空值为 ""# 横线连接pd.concat([pd1, pd2], axis=1)
参数
subset:column label or sequence of labels, optional用来指定特定的列,默认所有列keep:{‘first’, ‘last’, False}, default ‘first’删除重复项并保留第一次出现的项inplace:boolean, default False是直接在原来数据上修改还是保留一个副本
Demo
DataFrame.drop_duplicates(["", "", ""], keep='first', inplace=True) # 对 DataFrame 格式的数据,去除特定列的重复行,重复数据保留第一次出现的数据,就地去重import pandas as pddata = pd.DataFrame({'A' : [1,2,3,4], 'B' : ['a', 'b', 'a', 'b']})data.drop_duplicates('B', keep='first', inplace = True)"""print data结果A B0 1 a1 2 b"""
# 设置 index 或 columns 的 nameframe.index.name = xxx # 可以在 reset_index 的时候用到,给 index 起一个名字,然后就可以把 index 变成一个 columnframe.columns.name = yyy
注意:reindex 只是改变 pandas 数据按照 index 的排列顺序,并不是更改 index
frame.reindex(columns = [...], index=[...])# 如果没有指定 index 和 column,那么默认修改 indexframe.reindex(['a', 'b', 'c'], fill_value=0) # 如果 'c' 无对应的值,则为 Nan
注意,pd_data.index 和 pd_data.column 返回的都是一个 Iterable 对象,这样可以在适当的情况下节省内存
index_list = pd_data.index.tolist()column_list = pd_data.column.tolist()
可以通过 rename 重命名 column 和 index,需要注意的是,rename 默认并不是一个 inplace 函数,其仍然符合 cascade 法则,会开辟一块新的内存,最好加上 inplace=True
的参数。
# 修改 columnpd_whitelist = pd_whitelist.rename(columns = {"deploy_module": "module", "monitor_whitelist": "idc" }, inplace=True)
当我们对多个 DataFrame 进行计算时,往往需要两个 DataFrame 进行 Index 对齐,目前我发现的最简单的方法是:
pd_data = pd_data.reset_index().drop("index", axis=1) # index 为 index name# reset_index() 用于将 index 转成 column,然后 index 重新设置为 0,1,2,3
# 将某几列转成行索引pd_frame = pd_frame.set_index(["column1", "column2"])
# 获取字段对应的列的所有值,转成 listname = pd_data['姓名'] # 这里返回了 姓名 的Seriesname = pd_data.姓名.tolist()name_list = pd_data['姓名'].tolist()
pd_size = len(pd_data)
sort_values 已经替换了 sort_index 的功能,其可以对 index 和 column 进行排序。默认情况下,该方法对 index 进行排序,但是该方法已经可以根据 by
自动识别 index 和 column,我们不需要显式的指定 index 还是 value,但是为了严谨一点,我们还是加入 axis = 1,表示是对 column 进行排序
pandas_data = pandas_data.sort_values(by = ["created_time", "age"], ascending = [False,True], axis=1)
axis = 1: 添加列; axis = 0: 添加行
注意,如果我们合并的行列与 index 无关的话,那么最好加上 ignore_index
,否则 index 在合并的时候,可能会带来问题。
# 添加 行,pd_result = pd.concat([pd_result, pd_new_data], ignore_index=True, axis=1)# 添加列pd_result = pd.concat([pd_result, pd_new_data], ignore_index=True, axis=1)
# append 只能用来添加元组,并返回一个新的对象pd_vote_tor_train.append(pd_vote_for_test, ignore_index=True)
Drop specified labels from rows or columns. 返回 drop 之后的 pandas
# 删除两列pd_data = pd_data.drop(["vote_result", "score"], axis=1)# 删除行数据,frame.drop([index1, index2]) # 默认 drop 删除行数据 axis = 0 就是行
# 矩阵和图标的转置pd_demo = pd_demo.T
# isnull, notnull 检测缺失值pd.isnull(obj)pd.notnull(obj)
# 引入 numpy 的科学计算函数import numpy as npnp.log2(pd_data)np.log10(pd_data)np.exp(pd_data)# 求最大值最小值pd_demo.max()# 如果 pd_demo 是 DataFrame,则默认对列进行操作,获取每列的最大值,形成一个 series# 如果 pd_demo 是 Series,则返回最大值# Series 可以直接进行算术运算(pd_statistics_daycount * pd_sum_statistics).sort_values(ascending=False)# Series 在进行算术运算时可以自动对齐不同索引的数据,而笛卡尔积式的索引# applypd_data["meddle_time"] = (pd_data["meddle_end"].apply(lambda x: datetime.datetime.strptime(str(x), "%Y-%m-%d %H:%M:%S")) - \pd_data["meddle_start"].apply(lambda x: datetime.datetime.strptime(str(x), "%Y-%m-%d %H:%M:%S")))
map 方法需要传入一个 func,该 func 可以是 built-in function ,也可以是 lambda expression
pd_frame["animal"] = data["food"].map(str.lower).map(food_to_animal_dict)
# 对 Series 进行排序pd_statistics_daycount = pd.Series(statistics_daycount_dict).sort_values(ascending=False) # 按值进行排序pd_statistics_daycount = pd.Series(statistics_daycount_dict).sort_index(ascending=False) # 按索引进行排序
append 连接另一个index对象,返回一个新的indexdiff 计算差集intersection 计算交集union 计算并集
pd_demo_copy= pd.DataFrame(pd_demo) # pd_demo_copy 其实是 pd_demo 的一个浅拷贝,对 pd_demo_copy 的任何操作,当相当于直接对 pd_demo_copy 进行操作pd_demo_copy = pd_demo.copy(deep=True) # 这才是深拷贝
如果我们想要对 iterable objects 进行变换,那么就不需要将其先转成 list to_list()
再变换
obj.index # 返回 RangeIndex类型的可迭代的对象obj.values # 返回 numpy.ndarray 类型的可迭代的对象
大多数方法都有 inplace 选项,如果 inplace = True,就不会开辟额外的内存。例如 drop_duplicates 方法,就有 inplace 选项,下面列举一下带有 inplace 选项的方法:
renamedrop_duplicates
注意:inplace = True 后,返回的 pandas 就是 None,代码就应该改成:
df_one_column = df_one_column.drop_duplicates("weibo")# 改成df_one_column.drop_duplicates("weibo", inplace=True) # 返回 None
pandas 数据筛选,其功能等价于 sql 语句的 where 条件筛选。
官网文档:https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html
Python 的 index operator []
和 attribute operator .
在 pandas 中同样通用。但是在不知道传入数据的数据类型的情况下,使用 index/attribute 操作符往往会出现一些意想不到的问题,例如chained assignment
。所以我们要尽量用 Pandas 提供的 API 写 Pandasic 的代码,而不是 Pythonic。
总的来说,在系统报 SettingWithCopy
的错误时,我们应该将 chained assignment 改成 loc 的模式。
Object Type | Selection(Chained assignment) | Return Value Type |
Series |
| scalar value |
DataFrame |
|
|
Pandas 的数据筛选的大部分 API,都是 Series/DataFrame 通用的,所以同样一个 API,以下的示例通常有两个Demo
Pandas 中的 label-based slice 是包含尾部元素的
ix 已经被 iloc 和 loc 替换
注意,不能使用 attribute 的方式创建新列
# 添加新列pd_data["flag"] = "defaut_value"df.two = [4, 5, 6] # 会报 UserWarning
df1[lambda df: df.columns[0]] # 选取第一列
| 优点 | 缺点 |
| 简洁明了 | 1. 不能 slice 2. 会导致很多奇怪的问题 |
| 准确规范 | 不够简洁 |
DataFrame 的 slice 是对行进行 slice。
df[:3] # 对前三行进行 slicedf[::-1] # 按行逆置
loc 是根据 label 进行查找,label 包括 index 和 column。
Object Type | Indexers |
Series |
|
DataFrame |
|
Series
# 对 Series 进行切片s1 = pd.Series(np.random.randn(6), index=list('abcdef'))"""a 1.431256b 1.340309c -1.170299d -0.226169e 0.410835f 0.813850dtype: float64"""s1.loc['c':] # 从 c slice 到最后s1.loc['b'] # 获取 index 为 b 对应的值s1.loc['c':] = 0 # slice 后统一赋值# slice slice 是对 3 到 5 之间的元素进行 slice,并不是 对 3,4,5进行 slice,slice 是按照 index 顺序的s = pd.Series(list('abcde'), index=[0, 3, 2, 5, 4])s.loc[3:5] # 3,2,5s.sort_index() # 对数据按照 index 进行排序
DataFrame
df1 = pd.DataFrame(np.random.randn(6, 4),index=list('abcdef'),columns=list('ABCD'))df1.loc[['a', 'b', 'd'], :] # sample index 为 a,b,d 的 dataframedf1.loc['d':, 'A':'C'] # sample index 为 d, column 为 ABC 的 dataframedf1.loc['a'] # sample index 为 a 的 seriesdf1.loc['a', 'A'] # get a value in df1["a"]["A"]dfi.loc[:, 'C'] = dfi.loc[:, 'A'] # 新建 C 列,并将 A 列的数据赋值给 C 列
loc 是根据 position 进行 index 的。
Series
s1 = pd.Series(np.random.randn(5), index=list(range(0, 10, 2)))s1.iloc[:3] # 获取前三个数据s1.iloc[3] # 获取第四个数据s1.iloc[:3] = 0 # 对前三个数据进行赋值
DataFrame
df1 = pd.DataFrame(np.random.randn(6, 4),index=list(range(0, 12, 2)),columns=list(range(0, 8, 2)))df1.iloc[:3] # 获取前三行的 dfdf1.iloc[1:5, 2:4] # 获取第二行到第五行,第三列到第四列的 dfdf1.iloc[[1, 3, 5], [1, 3]] # 获取第 2,4,6 行,第2,4列的 dfdf1.iloc[1:3, :]df1.iloc[1] # 获取第二行的 series# assign a dict to a row of a DataFramex = pd.DataFrame({'x': [1, 2, 3], 'y': [3, 4, 5]})x.iloc[1] = {'x': 9, 'y': 99}
注意,当 slice 的界限超出了 pandas 的界限时,pandas 的处理方式 和 Python 的处理方式一样,都返回空,但是对于指定的 index,如果超出了界限,就会报 IndexError
在 select 的时候加入函数,实现更复杂的 select
df1 = pd.DataFrame(np.random.randn(6, 4),index=list('abcdef'),columns=list('ABCD'))"""A B C Da -0.023688 2.410179 1.450520 0.206053b -0.251905 -2.213588 1.063327 1.266143c 0.299368 -0.863838 0.408204 -1.048089d -0.025747 -0.988387 0.094055 1.262731e 1.289997 0.082423 -0.055758 0.536580f -0.489682 0.369374 -0.034571 -2.484478"""df1.loc[lambda df: df.A > 0, :] # 对 row 进行 select,条件为 列 A 的值 > 0"""A B C Dc 0.299368 -0.863838 0.408204 -1.048089e 1.289997 0.082423 -0.055758 0.536580"""df1.loc[:, lambda df: ['A', 'B']] # 选取 A,B 列df1.iloc[:, lambda df: [0, 1]] # 选取第一列和第二列df1[lambda df: df.columns[0]] # 选取第一列df1.A.loc[lambda s: s > 0] # 选取第一列中大于0的元素# 两列进行比较pd_candidate[pd_candidate.label != pd_candidate.predicted_label]
df_sample = df1.sample(n=5) # 随机采样 5 个元素df.head(n=10) # 选取头几个元素
[] .
,和 loc,iloc
都是先进行查找,再进行列查找,查找效率很低。如果想要快速定位到某一个位置的值,可以使用 at/iat
df1 = pd.DataFrame({'A':list("avb")})df1.iat[0,0] # 返回 adf1.iat[0,0] = "s" # 赋值
Operators: |for
or,
&for
and, and
~for
not`
注意,这些操作符必须被 ()
圈起来。
# seriess = pd.Series(range(-3, 4))s[s > 0] # 返回大于 0 的 seriess[(s < -1) | (s > 0.5)] # 返回一定范围的 series# dataframedf[df['A'] > 0]df2 = pd.DataFrame({'a': ['one', 'one', 'two', 'three', 'two', 'one', 'six'],'b': ['x', 'y', 'y', 'x', 'y', 'x', 'x'],'c': np.random.randn(7)})criterion = df2['a'].map(lambda x: x.startswith('t'))df2[criterion]"""a b c2 two y 0.0412903 three x 0.3617194 two y -0.238075"""df2[criterion & (df2['b'] == 'x')] # 多个条件"""a b c3 three x 0.361719"""df2.loc[criterion & (df2['b'] == 'x'), 'b':'c'] # 多多个条件"""b c3 x 0.361719"""pandas_selected = pandas_data[(pandas_data.status == 'fail') \& ((pandas_data.reason == 'online') \| (pandas_data.reason == 'known_online') \| (pandas_data.reason == 'deploy'))]# 否定的使用pandas_selected = pandas_data[~(pandas_data.status == 'fail')]
s = pd.Series(np.arange(5), index=np.arange(5)[::-1], dtype='int64')s.isin([2, 4, 6])"""4 False3 False2 True1 False0 Truedtype: bool"""s[s.isin([2, 4, 6])]"""2 20 4dtype: int64"""s[s.index.isin([2, 4, 6])] # 判断 index isin# dfpd_vote_for_test = pd_training_data[pd_training_data.hash_id.isin(test_hash_id)]# not inpd_vote_for_test = pd_training_data[~(pd_training_data.hash_id.isin(test_hash_id))]
where 和 boolen select 的区别:
Boolean select 出来的数据,通常是原 pandas 的子集,而 where 可以保留原 pandas 的结构。
s.where(s > 0)"""4 NaN3 1.02 2.01 3.00 4.0dtype: float64"""
DataFrame
objects have a query()
method that allows selection using an expression.
query() 的优点:
兼容 Python 的语法,更接近自然语言表达,例如其兼容 and/or/in/not in
DataFrame.query()
using numexpr
is slightly faster than Python for large frames.
df[(df.a < df.b) & (df.b < df.c)] # 等价于df.query('(a < b) & (b < c)') # 等价于df.query('a < b & b < c') # 等价于df.query('a < b and b < c')df.query('a in b') # 等价于df[df.a.isin(df.b)]df.query('a not in b') # 等价于df[~df.a.isin(df.b)]df.query('a in b and c < d')df.query('b == ["a", "b", "c"]') # simlilar to in ,等价于df[df.b.isin(["a", "b", "c"])]df.query('[1, 2] in c')df.query('[1, 2] not in c')
SettingWithCopy
warning !!! 的原因:
dfmi = pd.DataFrame([list('abcd'),list('efgh'),list('ijkl'),list('mnop')],columns=pd.MultiIndex.from_product([['one', 'two'],['first', 'second']]))"""one twofirst second first second0 a b c d1 e f g h2 i j k l3 m n o p"""dfmi.loc[:, ('one', 'second')] = value# 等价于dfmi.loc.__setitem__((slice(None), ('one', 'second')), value) # 一步到位dfmi['one']['second'] = value# 等价于dfmi.__getitem__('one').__setitem__('second', value) # 由于不确定 getitem 返回的对象是原对象还是新 copy 的对象,所以才会抛出 SettingWithCopy 的警告
stack, unstack
stack: 将 dataframe 转成 多重索引的 series
unstack: 将 多重索引的 series 转成 dataframe
pd_series.unstack("index1") # 将 index1 转成以 index1 为 column 的 dataframe
pivot
# pivot 用于将 信息量很大的长表(例如从数据库中读取的一张表),透视成一张宽表pd_frame.pivot("index_column", "column_column", "value")# 参数一:Column to use to make new frame’s index. If None, uses existing index.# 参数二:Column to use to make new frame’s columns.# 参数三:Column(s) to use for populating new frame’s values. If not specified, all remaining columns will be used and the result will have hierarchically indexed columns.# raise: When there are any index, columns combinations with multiple values. DataFrame.pivot_table when you need to aggregate.
pivot_table
用于将长表变短表
df = pd.DataFrame({"A": ["foo", "foo", "foo", "foo", "foo","bar", "bar", "bar", "bar"],"B": ["one", "one", "one", "two", "two","one", "one", "two", "two"],"C": ["small", "large", "large", "small","small", "large", "small", "small","large"],"D": [1, 2, 2, 3, 3, 4, 5, 6, 7]})'''dfA B C D0 foo one small 11 foo one large 22 foo one large 23 foo two small 34 foo two small 35 bar one large 46 bar one small 57 bar two small 68 bar two large 7'''table = pivot_table(df, values='D', index=['A', 'B'],columns=['C'], aggfunc=np.sum)'''tableC large smallA Bbar one 4.0 5.0two 7.0 6.0foo one 4.0 1.0two NaN 6.0'''table = pivot_table(df, values=['D', 'E'], index=['A', 'C'],aggfunc={'D': np.mean, 'E': [min, max, np.mean]})'''tableD Emean max median minA Cbar large 5.500000 16 14.5 13small 5.500000 15 14.5 14foo large 2.000000 10 9.5 9small 2.333333 12 11.0 8'''
# 过滤出 name 列包含有 pandas 的数据pd_frame = pd_frame[pd_frame.name.str.contains("pandas")] # 类似的进行判断的字符串方法还有 endswith, startswith# 对 name 列进行正则表达式的匹配,返回的是匹配到的 seriespd_series = pd_frame.name.str.match(pattern)"""index0 ["wansho", "wangkai"]index2 ["libohan", "guandonghai"]"""# 获取元素的第 几个 字符pd_series = pd_series.str.get(0)# join 元素列表pd_series = pd_series.str.join("-")# 判断 name 列的所有名字,某个字符串的出现次数pd_series = pd_frame.name.str.count("love")# 对某列的长度进行筛选df = df[df.A.str.len() > 2]# 其他函数lower(), upper()center() # 在字符串左右两边添加空白符repeat(3) # 字符串重复 3 次replace(pattern, str, regex=True) # 用 str 替换 pattern 匹配的模式split() # 根据分隔符或正则表达式对字符串进行拆分strip(), lstrip(), rstrip()len()
* merge 等价于数据库上的外连接,merge 是横向的连接* 等值连接的时候,如果按照左边为主,就是左外连接,如果按照右边为主,就是右外连接* 没有内连接* 自然连接 没有 how 属性* 外连接一共有三种:(都是等值连接)1. 外连接 how = 'out'2. 左外连接 how = 'left'3. 右外连接 how = 'right'* 参数 on 就是设置按照哪一列进行等值连接参考:https://blog.csdn.net/zhouwenyuan1015/article/details/77334889https://blog.csdn.net/starter_____/article/details/79198137Code Demo1:loss_data = pd.merge(pandas_data_deployed, pandas_data_called, how = 'left', on = columnsOfCoverageMerge)pandas_data_deployed_columns_len = len(pandas_data_deployed.columns) # 获取字段数 22 个col_y = loss_data.columns[pandas_data_deployed_columns_len] # 获取某个字段loss_data = loss_data[loss_data[col_y].isnull()] # 如果该字段为空(NaN),那么选取该组数据,该方法经常用来获取两张表关于 某一 字段 的差集如果是左连接,那么 isnull() 得到的就是关于某一字段的 A - B 的差集如果是右连接,那么 isnull() 得到的就是关于某一字段的 B - A 的差集注意如果是左连接,那么字段判空选择的字段 X 肯定是右边的那个表的非等值连接的某个字段,也就是可能会产生 空值 的字段。loss_data = loss_data[columnsOfCoverageMerge]Code Demo2:data1 = {'A': ['a1', 'a1', 'a2', 'a2'],'B': ['b1', 'b2', 'b3', 'b4'],'C': [5, 6, 8, 12]}data2 = {'B': ['b1', 'b2', 'b3', 'b3', 'b5'],'E': [3, 7, 10, 2, 2]}pandas_data1 = pd.DataFrame(data1)pandas_data2 = pd.DataFrame(data2)merge_data1 = pd.merge(pandas_data1, pandas_data2,how = 'left', on = ['B'])merge_data2 = pd.merge(pandas_data1, pandas_data2, how = 'outer',on = ['B']).fillna(0)print merge_data1[merge_data1['A'].isnull()]print merge_data1print merge_data2# left_on, right_onpd.merge(pd_left, pd_right, left_on=["name"], right_on=["姓名"])# suffixes 对于重复的字段,添加后缀pd.merge(pd_left, pd_right, on=[], how="left", suffixes=["_left", "_right"])# left_index, right_index, 将索引作为连接的键,类似于 left_on, right_on, 这四个参数可以混用pd.merge(pd_left, pd_right, left_on="key1", right_index=True)# how 参数的默认值为 inner,即内连接,其 merge 后的字段为两个 dataframe 的交集,left, right, outer 的字段为两个 dataframe 字段的并集# 多个 pandas 横向纵向连接pd.concat([pd1, pd2, pd3], axis=1, keys=["one", "two", "three"], ignore_index=True, names=["upper", "lower"])axis: 1 为横向连接,0 为纵向连接,默认纵向连接keys: 连接后,新加的二层索引ignore_index:是否不加入 indexnames: 给多层索引从上到下起名称pd.concat({"level1": pd1, "level2": pd2}) # 加入第二层索引的另一种方式
groupby 分两个步骤
分组
计算
################################### step1: 分组 #####################################* groupby 通过 某几列 进行分组pg_data = pandas_data.groupby(["status", "reason"], as_index=False) # 返回一个 DataFrameGroupBy 对象, 如果 as_index 为 False,则不会将 staus 和 reason 变成 indexgroupby 不会删除数据,只是对数据按照 N = |status| * |reason| 分成 N 组。分组后的数据条数不会变化。demo:pd_times_statistics = pandas_data_deployed[["deploy_plat", "module", "deploy_times"]].groupby(["deploy_plat", "module"]).count() # 生成一个 frame# 解析 pd_times_statistics 获取 key:deploy_plat + module, value: times 的字典module2times_dict = {','.join(index): row["deploy_times"] for index, row in pd_times_statistics.iterrows()}# 通过 字典 进行分组mapping = {"图搜": "大搜", "分级": "大搜", "人脸识别": "AI"}pd_data = pd_data.group(mapping).count()pd_data = pd_data.group(mapping, axis=1).count()# 通过多级索引的索引级别进行分组 levelimport pandas as pdimport numpy as npcolumns = pd.MultiIndex.from_arrays([["US", "US", "US", "JP", "JP"], [1,2,3,4,5]], names=["country", "number"])pd_data = pd.DataFrame(np.random.randn(4, 5), columns=columns)print(pd_data)pd_data = pd_data.groupby(level="country", axis=1).sum()print(pd_data)'''country US JPnumber 1 2 3 4 50 0.014945 0.498798 0.939967 1.053238 2.6891401 -0.092643 0.671471 0.255130 -0.035580 -1.4224672 -1.594973 0.753659 -0.983928 0.341066 0.9996923 -0.590248 -0.339360 -0.706315 0.277258 -1.921277country JP US0 3.742378 1.4537091 -1.458048 0.8339592 1.340759 -1.8252423 -1.644020 -1.635923'''################################### step2: 计算 ###################################### groupby 后的方法count()sum()mean()medain()std() # 标准差var() # 方差min()max()first(), last()quantile(num) # 获取分位数 0 <= num <= 1# groupby 自定义计算方法def peak_2_peak(series):return series.max() - series.min()grouped.agg(peak_2_peak)# groupby 后进行多函数计算grouped.agg(["mean", "count", "std"])# groupby 后对不同的列进行不同的计算方法grouped.agg({"age": "mean", "sex": ["sum", "count"]})# groupby 遍历for module, idc_group in dfp_pandas_data_called.groupby(["module"]): # module 是单个值或元组,idc_group 是 pandas dataframe 格式idcs = list(set(idc_group["idc"].tolist()))dfp_module2idcs_dict[module] = idcsgroup 后的group 其实是一个 pandas dataframe 对象,其中存放了除 module属性 外的所有属于 module 组的属性,如果只选取一部分属性,可以直接idc_group[["A", "B"]] # 这是 pandas 的一个语法糖# 注意groupby 后,groupby 的对象是唯一的,可以当 key 使用# Demoimport pandas as pddata1 = {'A': ['a1', 'a1', 'a2', 'a2', 'a3'],'B': ['b1', 'b1', 'b2', 'b2', 'b3'],'C': [5, 6, 8, 12, 1]}pd_frame = pd.DataFrame(data1)pd_statistics = pd_frame.groupby(['A','B']).count() # 注意结果是一个 DataFrame,有两个 index:A 和 B。C 变成了 count 字段。结果:CA Ba1 b1 2a2 b2 2a3 b3 1上述结果得到的是一个 DataFrame,但是其得到的是一个有两个 index 的dataframe,实际上,按照那几个参数进行了groupby,那么index就有几个,如果想要将 index 转换成列信息,那么可以用:pd_statistics.reset_index() # 将 两级 index 转成 列数据那么结果就变成了:A B C0 a1 b1 21 a2 b2 22 a3 b3 1其中,index 变成了 数字
官方文档
环境依赖
pip install sqlalchemypip install pymysql
写数据库Demo
# 写入 MySQLfrom sqlalchemy import create_engine# test 是 dbname ,localhost 指的是 IPengine = create_engine('mysql+pymysql://user1:123456@localhost:3306/test')# index 参数用于判断是否将 index 写入数据库pd_conf.to_sql(table_name, con=engine, if_exists="append", index=False)
注意
pandas 写入数据库时,如果数据表不存在,则会建立数据库,但是不会指定数据表的主键
读数据库Demo
http://pandas.pydata.org/pandas-docs/stable/generated/pandas.read_sql.html#pandas.read_sql
read_sql 是 对 read_sql_table 和 read_sql_query 的封装。
对于非文本的文件的读取,excel 中存储的是什么格式,pandas 就读取的什么格式,如果一串数字以字符串的形式存储,其读到pandas中就是一个字符串,如果一列单元格存储的是时间,那么 pandas 读取后就是 datetime。
from openpyxl import load_workbookdef init_excel(self):"""创建一个空的 Excel 文件"""pd_empty = pd.DataFrame()try:pd_empty.to_excel(self.excel_path)except Exception as e:logging.error("init excel failed, because {e}".format(e=e))logging.info("init excel success")def to_excel(self, pd_data, sheet_name):"""将 pandas 写入 Excel 的指定的 sheet 中"""excel_writer = pd.ExcelWriter(self.excel_path, engine='openpyxl')# 写入 Exceltry:book = load_workbook(excel_writer.path)excel_writer.book = bookpd_data.to_excel(excel_writer=excel_writer, sheet_name=sheet_name, index=False)excel_writer.close()except Exception as e:logging.error("pandas into excel failed, because {e}".format(e=e))logging.info("pandas into excel success")# 读取 excelpd_demo = pd.read_excel(path)
pandas 读取文本文件时,对于所有的数字内容,读取后都会转成数字,不管其读取之前是否为字符串
# 读取 csv 文件pd.read_csv(path, header=None, index_col=["col1", "col2"]) # 不读取列名,并选择两列作为索引pd.read_csv(path, error_bad_lines=False) # 跳过错误行# 注意 如果文件中有数字类型的数据,那么统一转成 long 类型,即使存储前是 str 类型,那么读取出来也是 数字类型# to_csv()# 将 DataFrame 表存储成 csv 格式deploy_cfg.to_csv("file.csv", encoding = 'utf_8_sig') # 注意文件名不要写中文pd_data.to_csv("test.csv", mode="a", header="False") # 增量模式,不加 header
TimeSeries初始化
import pandas as pdimport numpy as npfrom datetime import datetime# TimeSeries 初始化,传入的 index 必须是 datetime 类型的 listindex = [datetime(2019, 1, 1), datetime(2019, 2, 2), datetime(2019, 3, 3), datetime(2021, 1, 3)] # DatetimeIndex 类型pd_time = pd.Series(range(len(index)), index=index)pd_time.index
DatetimeIndex(['2019-01-01', '2019-02-02', '2019-03-03', '2021-01-03'], dtype='datetime64[ns]', freq=None)
TimeSeries切片、选取、索引
# 模糊选取pd_time["2019"]
2019-01-01 02019-02-02 12019-03-03 2dtype: int64
# 精确获取某个 index 对应的值pd_time[datetime(2019,1,1)]
0
# 按照给定时间范围切片pd_time["2019-01-01": "2019-03-01"]
2019-01-01 02019-02-02 1dtype: int64
生成指定长度的 DatetimeIndex: date_range
pd.date_range("2019-01", "2020-01") # 默认的时间间隔是 天,注意:date_range 生成的是时间戳,而不是时间段
DatetimeIndex(['2019-01-01', '2019-01-02', '2019-01-03', '2019-01-04','2019-01-05', '2019-01-06', '2019-01-07', '2019-01-08','2019-01-09', '2019-01-10',...'2019-12-23', '2019-12-24', '2019-12-25', '2019-12-26','2019-12-27', '2019-12-28', '2019-12-29', '2019-12-30','2019-12-31', '2020-01-01'],dtype='datetime64[ns]', length=366, freq='D')
# 生成由每月最后一个工作日组成的日期索引pd.date_range(start="2019-01", end="2019-12", freq="BM")
DatetimeIndex(['2019-01-31', '2019-02-28', '2019-03-29', '2019-04-30','2019-05-31', '2019-06-28', '2019-07-31', '2019-08-30','2019-09-30', '2019-10-31', '2019-11-29'],dtype='datetime64[ns]', freq='BM')
# 由开始时间和持续时间生成的 indexpd.date_range(start="2019-01", periods=10) # period 指定了按照默认的 freq天级生成 10 组数据
DatetimeIndex(['2019-01-01', '2019-01-02', '2019-01-03', '2019-01-04','2019-01-05', '2019-01-06', '2019-01-07', '2019-01-08','2019-01-09', '2019-01-10'],dtype='datetime64[ns]', freq='D')
# 频率和日期偏移量# 按照 6hmin 的间隔生成 10 组时间戳, 注意: freq 可以写成任意时间间隔,P314pd.date_range(start="2019-01", periods=10, freq="6h20min")
DatetimeIndex(['2019-01-01 00:00:00', '2019-01-01 06:20:00','2019-01-01 12:40:00', '2019-01-01 19:00:00','2019-01-02 01:20:00', '2019-01-02 07:40:00','2019-01-02 14:00:00', '2019-01-02 20:20:00','2019-01-03 02:40:00', '2019-01-03 09:00:00'],dtype='datetime64[ns]', freq='380T')
period_range
# PeriodIndex 类型index_period = pd.period_range("2019-01-01", "2019-01-12", freq="D")pd.period_range("2019-01-01", "2019-01-12", freq="D")
PeriodIndex(['2019-01-01', '2019-01-02', '2019-01-03', '2019-01-04','2019-01-05', '2019-01-06', '2019-01-07', '2019-01-08','2019-01-09', '2019-01-10', '2019-01-11', '2019-01-12'],dtype='period[D]', freq='D')
# index 为 period 的 timeseries 的构造pd_period = pd.Series(range(len(index_period)), index=index_period)pd.Series(range(len(index_period)), index=index_period)
2019-01-01 02019-01-02 12019-01-03 22019-01-04 32019-01-05 42019-01-06 52019-01-07 62019-01-08 72019-01-09 82019-01-10 92019-01-11 102019-01-12 11Freq: D, dtype: int64
# 一时间段内的任意时间都可以作为 时间戳,化为 indexpd_period["2019-01-01 19:23:00"] = 100pd_period
2019-01-01 1002019-01-02 12019-01-03 22019-01-04 32019-01-05 42019-01-06 52019-01-07 62019-01-08 72019-01-09 82019-01-10 92019-01-11 102019-01-12 11Freq: D, dtype: int64
时间戳重采样
pd_time.resample("Y").sum()
2019-12-31 32020-12-31 3Freq: A-DEC, dtype: int64
# 时间戳重采样index_timestamp = pd.date_range("2019-01-01", periods=12, freq="T")pd_time = pd.Series(range(len(index_timestamp)), index=index_timestamp) # 时间戳最细粒度的数据pd_time.resample("5min").sum().index
DatetimeIndex(['2019-01-01 00:00:00', '2019-01-01 00:05:00','2019-01-01 00:10:00'],dtype='datetime64[ns]', freq='5T')
# 通过 groupby 和 lambda 函数进行重采样index_range = pd.date_range("2019-01-01", periods=100, freq="D")pd_time = pd.Series(range(len(index_range)), index=index_range)pd_time.groupby(lambda x: x.month).sum()
1 4652 12463 22944 945dtype: int64
时间段重采样
pd_frame = pd.DataFrame(np.random.randn(12, 4),index=pd.period_range("2010-01", "2011-12", freq="2M"),columns=["Colorado", "Texas", "New York", "Ohio"])pd_frame.resample("D").mean().index # 升采样
PeriodIndex(['2010-01-01', '2010-01-02', '2010-01-03', '2010-01-04','2010-01-05', '2010-01-06', '2010-01-07', '2010-01-08','2010-01-09', '2010-01-10',...'2011-12-22', '2011-12-23', '2011-12-24', '2011-12-25','2011-12-26', '2011-12-27', '2011-12-28', '2011-12-29','2011-12-30', '2011-12-31'],dtype='period[D]', length=730, freq='D')
重采样参数
A yearM monthW weekD dayH hourT minuteS secondDemo:resample_time = "5min" # 5 分钟采样一次resample_time = "1H" # 1 hour 采样一次resample_time = "1D" # 1 天采样一次pd_result = pd_result.resample(resample_time, how="sum")
pd_data.sample(n) # n 一定要是非负整数,否则会报该错误