前言:
无论是做数据分析还是机器学习的特征工程,pandas绝对一个完成数据清洗、数据处理,甚至于数据分析的利器。
本文将基于Pandas 0.20.0版本展开
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pandas==0.20.0
一、Series和Dataframe
Series和Dataframe是Pandas中2种重要的数据类型。
1.Series是什么?
Series就是带有Index标签的一维数组, 是Dataframe中的1列数据,这列数据可以存储整型、浮点型、字符串甚至于Python对象。
列里面的每1个元素都对应1个Index,所以和Python中字典的数据结构很像。我们就可以根据Key是Index,Value是Series中的元素。
import pandas as pd def f1(a): print(a) #1.创建1个series print(pd.Series([f1,1,"我"],index=["a","b","c"])) ''' a <function f1 at 0x00000000005DC268> b 1 c 我 ''' #像字典一样根据key获取value print(pd.Series([f1,1,"我"],index=["a","b","c"])["c"]) #直接把字典变成Series data_dict={"a":1,"b":2,"c":3,"d":4} #我们还可以给Series进行命名 dict_series=pd.Series(data_dict,name="Series_name") #进行名称修改 dict_series.rename("new_name",inplace=True) #我们还可以获取Series中的中位数、最大值以及逻辑运算 print("中位数\n",dict_series.median()) print("大于中位数\n",dict_series[dict_series>dict_series.median()]) print("最大\n",dict_series.max())
2.Dataframe是什么?
Dataframe由多个不同的Series组成,所以它是有行和列2个维度组成的二维表格。
import pandas as pd df = pd.DataFrame({ "name": ["熊阔海", "秦琼", "李元霸", "宇文成都"], "age": [40, 30, 20, 28], "dynasty": ["隋朝", "唐朝", "隋朝", "隋朝"]}) print(df) #series.Series print(type(df['name'])) #frame.DataFrame print(type(df[['name',"age"]])) #series.Series print(df.iloc[3]) #frame.DataFrame print(df.iloc[[2,3]])
二、Pandas读取源数据
没有数据 怎么做分析呢?数据可能需要您自己创建,也可能是其他数据源,所以Pandas可以从各种数据源中导入数据, EXCEL/ CSV/ 统计软件/数据库.....
1.手工建立数据框
import pandas as pd pd.options.display.max_rows=10 #配置最多输出多少行 #新建数据库框 df1=pd.DataFrame({ 'var1':1.0, 'var2':[5,2,0,1,3,1,4], 'var3':'张根' #常数 }) ''' var1 var2 var3 0 1.0 5 张根 1 1.0 2 张根 2 1.0 0 张根 3 1.0 1 张根 4 1.0 3 张根 5 1.0 1 张根 6 1.0 4 张根 '''
df1=pd.DataFrame(data=[[1,"test"],[2,"train"],[3,'test'],[4,'train']],columns=['var2','va3']) #columns 列 ''' var2 va3 0 1 test 1 2 train 2 3 test 3 4 train ''' print(df1)
1.1字典转换成Dataframe
def process_item(self, item, spider): if spider.name=="HardwareSpider": import pandas as pd #字典变成.DataFrame data=pd.DataFrame([item]) print(data)
2.Pandas文件读取
Pandas提供了2个读取文件方法,其中read_csv()可以用于读取文本文件,read_excel()用于读取Excel格式文件;
filepath_or_buffe: 打开文件路径
sep: 设置列分隔符 ','
head: 设置从第几行开始
usecols: 设置使用的列
na_values: 设置为 NaN 缺失值
2.1 read_csv()
可读取csv格式文件,默认以' ,' 分割为 1列,注意read_csv()方法不仅可以读取CSV格式的文件,它通用于文本文件的读取;
import pandas as pd pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('univ.csv',encoding='gbk') #注意文件名称 1定要是英文 print(df1) ''' 名次 学校名称 总分 类型 所在省份 所在城市 办学方向 主管部门 0 1 北京大学 100.00 综合 北京 北京市 中国研究型 教育部 1 2 清华大学 98.50 理工 北京 北京市 中国研究型 教育部 2 3 复旦大学 82.79 综合 上海 上海市 中国研究型 教育部 3 4 武汉大学 82.43 综合 湖北 武汉市 中国研究型 教育部 4 5 浙江大学 82.38 综合 浙江 杭州市 中国研究型 教育部 .. ... ... ... .. ... ... ... ... 95 96 浙江师范大学 63.37 师范 浙江 金华市 区域特色研究型 浙江省 96 97 安徽大学 63.34 综合 安徽 合肥市 区域研究型 安徽省 97 98 首都医科大学 63.32 医药 北京 北京市 区域特色研究型 北京市 98 99 江南大学 63.31 综合 江苏 无锡市 区域特色研究型 教育部 99 100 山西大学 63.29 综合 山西 太原市 区域研究型 山西省 '''
有时候我的csv数据并非来自*.csv文件而是来自API,莫慌;
import requests import pandas as pd from io import StringIO from micro_crm.update_name_script import obtain_token sa = { 'url': 'http://39.107.6.24:8107', # 'https://vipcloud2.cloud.sensorsdata.cn', 'project': 'production', # 'zhupj', 'username': 'admin', 'password': 'UF0d6O' } class ExcelHandler(object): def __init__(self,file='D:\数据埋点方案标准模板1.xlsx'): self.file_name=file self.excel= pd.ExcelFile(self.file_name) def parser_excel(self,sheet,ignore=1,*args,**kwargs): ''' :param sheet: #sheet名称 :param args: #按照该列drop_nan :param kwargs: #{'excel列关包含的键字':'datafreme列的别名' } :return: df = obj.parser_excel('物品属性值校验表', 2, *['property_cn'], **{"property_cn": '名称', "property_enum": '枚举值'}) df = obj.parser_excel('item', 3, *['property_en'],**{"property_en": '属性英文名', "property_cn": '属性中文名', 'enumeration_value': "枚举值","enumeration_flag": '需要进行枚举值校验的字段'}) ''' df = self.excel.parse(sheet,header=None) index_col = [] rows, cols = df.shape for col in range(cols): if sum(df[col].isin(list(kwargs.values()))) > 0: index_col.append(col) if not index_col: return pd.DataFrame() df = df[index_col] df.columns = list(kwargs.keys()) df = df.dropna(subset=args)[ignore:] return df class GoodsCheckHandler(ExcelHandler):#物品表 def __init__(self,file='D:\数据埋点方案标准模板1.xlsx'): super().__init__(file='D:\数据埋点方案标准模板1.xlsx') def get_search_columns(self): item_df = obj.parser_excel('item', 3, *['property_en'], **{"property_en": '属性英文名', "property_cn": '属性中文名', 'enumeration_value': "枚举值", "enumeration_flag": '需要进行枚举值校验的字段'}) filed_info=list(item_df.loc[item_df.enumeration_flag =="T"].property_en.values) filed_info.extend(['item_type','category'])#'item_id','item_type'为固定的item表搜素列 return filed_info def item_search_aip(self,**kwargs): params = { 'project': kwargs.get('project'), 'q':self.sql, 'format':'csv' #'csv', } headers = { 'sensorsdata-token':self.token, } site_url = kwargs.get('url') + '/api/sql/query' data = requests.get(url=site_url, params=params, headers=headers).text data=data.replace('\t',',') try: df=pd.read_csv(filepath_or_buffer=StringIO(data),usecols=self.get_search_columns(), encoding='utf-8') except Exception: print('解析API数据失败,请检查列名') df=pd.DataFrame() return df def get_search_column_values(self): fileds=','.join(self.get_search_columns()) sql = 'select {0} from items;'.format(fileds)#.format(fileds) self.sql=sql self.token=obtain_token(sa)['token'] df=self.item_search_aip(**sa) return df obj=GoodsCheckHandler() print(obj.get_search_column_values())
2.2 read_excel()
读取excle,以 ' tab' 分割为1列
sheet_name:指定sheet名称
df1=pd.read_excel('aa.xlsx',sheet_name=0,header=0)
循环遍历excel对象
df = pd.read_excel(io="pandas.xlsx", header=None) for index, item in df.iterrows():#index:索引,每一行 print(index,item)
class ExcelHandler(object): def __init__(self,file='D:\数据埋点方案标准模板1.xlsx'): self.file_name=file self.excel= pd.ExcelFile(self.file_name) def parser_excel(self,sheet,ignore=1,*args,**kwargs): ''' :param sheet: #sheet名称 :param args: #按照该列drop_nan :param kwargs: #{'excel列关包含的键字':'datafreme列的别名' } :return: df = obj.parser_excel('物品属性值校验表', 2, *['property_cn'], **{"property_cn": '名称', "property_enum": '枚举值'}) df = obj.parser_excel('item', 3, *['property_en'],**{"property_en": '属性英文名', "property_cn": '属性中文名', 'enumeration_value': "枚举值","enumeration_flag": '需要进行枚举值校验的字段'}) ''' df = self.excel.parse(sheet,header=None) index_col = [] rows, cols = df.shape for col in range(cols): if sum(df[col].isin(list(kwargs.values()))) > 0: index_col.append(col) if not index_col: return pd.DataFrame() df = df[index_col] df.columns = list(kwargs.keys()) df = df.dropna(subset=args)[ignore:] return df
2.3 read_sql()
读取数据库数据
sql:需要执行的sql / 要读取的表名
con:sqlalchemy 创建的 数据库连接名称
index_col:作为索引列 的列名
columns:显示的列
import pandas as pd from sqlalchemy import create_engine pd.options.display.max_rows=10 #配置最多输出多少行 mysql_con=create_engine('mysql+pymysql://webproject:xxxxxx1234@172.17.10.112:3306/web') oracle_conn=create_engine('oracle+cx_oracle://scott:tiger@172.16.22.16:1521/ecdb') data=pd.read_sql(sql='cmdb_worker_order',con=mysql_con,index_col='seach',columns=['id','initiator','alarm_time',]) ''' sql:需要执行的sql / 要读取的表名 con:sqlalchemy 创建的 数据库连接名称 index_col:作为索引列 的列名 ,columns:显示的列 ''' print(data)
三、Pandas 输出数据
有输入就得有输出,如何把pandas读取到的数据 输出保存到外部文件?
1.输出到.csv格式文件
import pandas as pd from sqlalchemy import create_engine pd.options.display.max_rows=10 #配置最多输出多少行 mysql_con=create_engine('mysql+pymysql://webproject:xxxxxx1234@172.17.10.112:3306/web') oracle_conn=create_engine('oracle+cx_oracle://scott:tiger@172.16.22.16:1521/ecdb') data_frame=pd.read_sql(sql='cmdb_worker_order',con=mysql_con,index_col='seach',columns=['id','initiator','alarm_time',]) data_frame.to_csv(path_or_buf='zhanggen.csv',sep='|',columns=['id','initiator','alarm_time'],header=['ID','发起人','发起时间'],index=True,mode='w',encoding='utf-8') ''' path_or_buf: 写入文件的路径 columns: 写入的列们,使用列表指定 header: 指定列名 index: 是否导出索引列(默认为True) mode: Python 写文件的模式 encoding: 指定编码 '''
2.输出到Excel文件
原来 把数据库里的数据 导出到Excel,还可以这么简单!
import pandas as pd from sqlalchemy import create_engine pd.options.display.max_rows=10 #配置最多输出多少行 mysql_con=create_engine('mysql+pymysql://webproject:xxxxxx1234@172.17.10.112:3306/web') oracle_conn=create_engine('oracle+cx_oracle://scott:tiger@172.16.22.16:1521/ecdb') data_frame=pd.read_sql(sql='cmdb_worker_order',con=mysql_con,index_col='seach',columns=['id','initiator','alarm_time',]) data_frame.to_excel(excel_writer='zhanggen.xlsx',sheet_name='工单数据' ,columns=['id','initiator','alarm_time'],header=['ID','发起人','发起时间'],index=True,encoding='utf-8') ''' excel_writer: Excel路径 sheet_name: 指定Excel中sheet名称 columns: 写入的列们,使用列表指定 header: 指定列名 index: 是否导出索引列(默认为True) encoding: 指定编码 '''
追加写
startrow=指定从第几行开始写
s_df.to_excel(excel_writer=writer, sheet_name='事件表&物品表match校验', index=False,encoding='utf-8',columns=['os','app_version','event','probability'],header=['应用操作系统','应用版本号','事件名称','拼表成功率'])#,startrow=10
us_df.to_excel(excel_writer=writer, sheet_name='事件表&物品表match校验', index=False,encoding='utf-8',startrow=len(s_df)+10,columns=['os','app_version','event','item_type','e_item_id'], header=['应用操作系统','应用版本号','事件名称','事件表中item_type名称','事件表中item_id'])#,startrow=10 writer.save() writer.close()
3.输出到数据库
原来pandas还可以做数据库备份!原来把Excel数据导入数据里面也如此简单!
确保数据 主键不重复!
import pandas as pd from sqlalchemy import create_engine pd.options.display.max_rows=10 #配置最多输出多少行 mysql_con=create_engine('mysql+pymysql://webproject:xxxxxx1234@172.17.10.112:3306/web') backup_mysql_con=create_engine('mysql+pymysql://webproject:web@192.168.1.18:3306/web') oracle_conn=create_engine('oracle+cx_oracle://scott:tiger@172.16.22.16:1521/ecdb') data_frame=pd.read_sql(sql='cmdb_worker_order',con=mysql_con,index_col='seach',columns=['id','initiator','alarm_time',]) data_frame.to_sql(name='cmdb_worker_order',con=backup_mysql_con,if_exists='replace',index=True,) ''' name: 表名 con: 数据库连接对象 if_exists: fail 如果表已经存在:fail 不做任何处理 replace 删除原表重建新表 append 在原表之后插入新数据 index :是否导出索引 True '''
4.输出为HTML文件
如何把一个个Excel文件,通过 前端网页显示出来呢?
import pandas as pd bj08=pd.read_csv(filepath_or_buffer='PM25\Beijing_2008_HourlyPM2.5_created20140325.csv',header=2,usecols=[0,2,3,4,5,6,7,9,10]) bj09=pd.read_csv(filepath_or_buffer='PM25\Beijing_2009_HourlyPM25_created20140709.csv',header=2,usecols=[0,2,3,4,5,6,7,9,10]) bj10=pd.read_csv(filepath_or_buffer='PM25\Beijing_2010_HourlyPM25_created20140709.csv',header=2,usecols=[0,2,3,4,5,6,7,9,10]) bj11=pd.read_csv(filepath_or_buffer='PM25\Beijing_2011_HourlyPM25_created20140709.csv',header=2,usecols=[0,2,3,4,5,6,7,9,10]) bj12=pd.read_csv(filepath_or_buffer='PM25\Beijing_2012_HourlyPM2.5_created20140325.csv',header=2,usecols=[0,2,3,4,5,6,7,9,10]) bj13=pd.read_csv(filepath_or_buffer='PM25\Beijing_2013_HourlyPM2.5_created20140325.csv',header=2,usecols=[0,2,3,4,5,6,7,9,10]) bj14=pd.read_csv(filepath_or_buffer='PM25\Beijing_2014_HourlyPM25_created20150203.csv',header=2,usecols=[0,2,3,4,5,6,7,9,10]) bj15=pd.read_csv(filepath_or_buffer='PM25\Beijing_2015_HourlyPM25_created20160201.csv',header=2,usecols=[0,2,3,4,5,6,7,9,10]) bj16=pd.read_csv(filepath_or_buffer='PM25\Beijing_2016_HourlyPM25_created20170201.csv',header=2,usecols=[0,2,3,4,5,6,7,9,10]) bj=bj08.append(other=[bj09,bj10,bj11,bj12,bj13,bj14,bj15,bj16],ignore_index=True,verify_integrity=True) print(bj.to_html())
5.输出为json数据
竟然还可以把dataframe转成josn数据
xls = pd.ExcelFile('pandas.xlsx') df = xls.parse( header=None) df.columns=['name','age','gender'] # df=df.to_json(orient="records") print(df.to_json())
6.随机抽样
有时候我们不需要使用全部数据进行分析统计,所以我们可以按照比例、数据条目对整体数据进行抽样。
import pandas as pd df = pd.DataFrame([ [1, 2, 3, 4], [2, 2, 33, 44], [3, 2222, 333, 444], [4, 2222, 3333, 4444] ], columns=['col1', 'col2', 'col3', 'col4'], ) # 列名 # 随机抽样 print(df.sample(frac=0.5, random_state=None)) """ n:指定抽取多少行 frac:指定抽取行的比例 random_state: random_state=None抽取的数据不重复,random_state=1抽取的数据可重复。 """
四、DataFrame普通列操作
数据清洗是什么?就是修改一下列名、修改一下列的数据类型。。。。。。。。。
1.查看datafreme数据
Pandas读取到的数据之后 会把数据转换成DataFrame数据类型;
import pandas as pd from sqlalchemy import create_engine pd.options.display.max_rows=10 #配置最多输出多少行 mysql_con=create_engine('mysql+pymysql://webproject:xxxxxx1234@172.17.10.112:3306/web') backup_mysql_con=create_engine('mysql+pymysql://webproject:web@192.168.1.18:3306/web') oracle_conn=create_engine('oracle+cx_oracle://scott:tiger@172.16.22.16:1521/ecdb') data_frame=pd.read_sql(sql='cmdb_worker_order',con=mysql_con,index_col='seach',columns=['id','initiator','alarm_time',]) print(data_frame.info()) ''' <class 'pandas.core.frame.DataFrame'> Index: 2434 entries, 201803280808 to 201901290053203724928934 Data columns (total 3 columns): id 2434 non-null int64 initiator 2434 non-null object alarm_time 2434 non-null object dtypes: int64(1), object(2) memory usage: 76.1+ KB None '''
import pandas as pd from sqlalchemy import create_engine pd.options.display.max_rows=10 #配置最多输出多少行 mysql_con=create_engine('mysql+pymysql://webproject:xxxxxx1234@172.17.10.112:3306/web') backup_mysql_con=create_engine('mysql+pymysql://webproject:web@192.168.1.18:3306/web') oracle_conn=create_engine('oracle+cx_oracle://scott:tiger@172.16.22.16:1521/ecdb') data_frame=pd.read_sql(sql='cmdb_worker_order',con=mysql_con,index_col='seach',columns=['id','initiator','alarm_time',]) print(data_frame.head(8))# 浏览前8条
import pandas as pd from sqlalchemy import create_engine pd.options.display.max_rows=10 #配置最多输出多少行 mysql_con=create_engine('mysql+pymysql://webproject:xxxxxx1234@172.17.10.112:3306/web') backup_mysql_con=create_engine('mysql+pymysql://webproject:web@192.168.1.18:3306/web') oracle_conn=create_engine('oracle+cx_oracle://scott:tiger@172.16.22.16:1521/ecdb') data_frame=pd.read_sql(sql='cmdb_worker_order',con=mysql_con,index_col='seach',columns=['id','initiator','alarm_time',]) print(data_frame[6:18]) #dataframe 还支持切片
import pandas as pd from sqlalchemy import create_engine pd.options.display.max_rows=10 #配置最多输出多少行 mysql_con=create_engine('mysql+pymysql://webproject:xxxxxx1234@172.17.10.112:3306/web') backup_mysql_con=create_engine('mysql+pymysql://webproject:web@192.168.1.18:3306/web') oracle_conn=create_engine('oracle+cx_oracle://scott:tiger@172.16.22.16:1521/ecdb') data_frame=pd.read_sql(sql='cmdb_worker_order',con=mysql_con,index_col='seach',columns=['id','initiator','alarm_time',]) print(data_frame.tail(8))# 浏览后8条
2.修改列名称
import pandas as pd pd.options.display.max_rows=10 #配置最多输出多少行 data_frame=pd.read_csv(filepath_or_buffer='univ.csv',encoding='gbk') print(data_frame.columns) #查看datafreme的列属性 data_frame.columns=['名次1', '学校名称1', '总分1', '类型1', '所在省份1','所在城市1', '办学方向1','主管部门1']#修改修属性。 data_frame.rename(columns={'总分1':'总分6'},inplace=True) # 利用字典修改指定的列,inplace=True 直接修改元数据 print(data_frame.columns) data_frame.columns=['名次', '学校名称', '总分', '类型', '所在省份','所在城市', '办学方向','主管部门'] print(data_frame.columns)
3.筛选列
import pandas as pd pd.options.display.max_rows=10 #配置最多输出多少行 data_frame=pd.read_csv(filepath_or_buffer='univ.csv',encoding='gbk') # print(data_frame.名次) print(data_frame[['所在省份','名次','学校名称']])
4.删除列
import pandas as pd pd.options.display.max_rows=10 #配置最多输出多少行 data_frame=pd.read_csv(filepath_or_buffer='univ.csv',encoding='gbk') d3=data_frame.drop(columns=['名次','主管部门'],inplace=False) #inplace=False 不修改源数据 del d3['名次','主管部门'] #直接删除元数据列,尽量少这么干 print(d3)
5.列数据类型转换
import pandas as pd pd.options.display.max_rows=10 #配置最多输出多少行 data_frame=pd.read_csv(filepath_or_buffer='univ.csv',encoding='gbk') print(data_frame.dtypes) #查看列的 数据类型 object 字符串变量 print(data_frame.astype(dtype='str',copy=True,errors='raise').dtypes) print(data_frame.名次.astype(dtype='int',copy=True,errors='raise').dtypes)#修改指定列(名次列) ''' dtype: 指定转成的数据类型 copy=True 是否生成新的副本,而不是替换原数据 errors="raise"/"ignore" 转换时出错误是否抛出 raise,忽视ignore '''
6.合并列
我们如何根据多列的数据,把多个列进行合并呢?
首先来了解Python里面一个内置函数zip,zip总能返回1个二维数组。
>>> a = [1,2,3] >>> b = [4,5,6] >>> c = [4,5,6,7,8] >>> zipped = zip(a,b) # 打包为元组的列表 [(1, 4), (2, 5), (3, 6)] >>> zip(a,c) # 元素个数与最短的列表一致 [(1, 4), (2, 5), (3, 6)] >>> zip(*zipped) # 与 zip 相反,可理解为解压,返回二维矩阵式 [(1, 2, 3), (4, 5, 6)]
列合并操作
import pandas as pd fp_df = pd.DataFrame({'errType': ['?', '', '', '', '--'], 'ruleType': ['', '', '', '', ''], 'modelType': ["ILF", "ILF", "EI", "EO", 'EIF'] }) """ errType ruleType modelType 0 ? ILF 1 ILF 2 EI 3 EO 4 -- EIF -------------------------------------- 0 (R, ?) 1 (M, ILF) 2 (M, EI) 3 (M, EO) 4 (R, --) ----------------------------------------- errType ruleType modelType judgeType category 0 ? ILF R ? 1 ILF M ILF 2 EI M EI 3 EO M EO 4 -- EIF R -- ------------------------------------------------------ judgeType category 0 R ? 1 M ILF 2 M EI 3 M EO 4 R -- """ def model_merge(row): ''' :param row: :return: 判断匹配哪1种规则?规则匹配的优先顺序是: errType(R)---->ruleType(R)---->modelType(M) ''' if row['errType']: return 'R', row['errType'] if row['ruleType']: return 'R', row['ruleType'] return 'M', row['modelType'] print(fp_df) #获取1个包含2维元组的序列 two_series = fp_df[['errType', 'ruleType', 'modelType']].apply(model_merge, axis=1) print(two_series) print(type(two_series))#pandas.core.series.Series """ 0 (R, ?) 1 (M, ILF) 2 (M, EI) 3 (M, EO) 4 (R, --) """ two_dimensional_list = zip(*two_series) # zip(*)把series(2个列),变成二维数组 """ [(R,M,M,M,R),(?,ILF,EI,EO,--)] """ #新增2个新列 fp_df['judgeType'], fp_df['category'] = two_dimensional_list """ errType ruleType modelType judgeType category 0 ? ILF R ? 1 ILF M ILF 2 EI M EI 3 EO M EO 4 -- EIF R -- """ #删除3个旧列,完成3列---->2列的合并。 fp_df.drop(columns=['errType', 'ruleType', 'modelType'], inplace=True) """ judgeType category 0 R ? 1 M ILF 2 M EI 3 M EO 4 R -- """ print(fp_df)
五、DataFrame索引列操作
只要数据量大了 就会 出现查询慢的问题,因此Pandas的DataFrame数据也支持 通过索引加速;
1.创建索引
所有的 data frame在建立的时候,天然拥有流水号(0、1、2、3.......)索引,因此这里的 创建索引指定是 自定义索引列;
注意:即使是自定义的索引也 应当避免重复,虽然索引重复程序不会报错,但是会影响数据分析过程;
创建DataDrame时手工创建索引列
import pandas as pd pd.options.display.max_rows=10 #配置最多输出多少行 # data_frame=pd.read_csv(filepath_or_buffer='univ.csv',encoding='gbk') df1=pd.DataFrame({'年龄':18,'战斗力':[90,89,98,100,],'昵称':['老狗','猴儿','大占','豆儿'],'辈分':'小弟'}) print(df1) ''' 年龄 战斗力 昵称 辈分 18 1 老狗 小弟 18 2 猴儿 小弟 18 3 大占 小弟 18 4 豆儿 小弟 ''' #所有的 data frame在建立的时候,天然拥有流水号索引,因此这里的 创建索引指定是 自定义索引,注意即使是自定义的索引也不能重复 df2=pd.DataFrame({'年龄':18,'战斗力':[90,89,98,100,],'昵称':['老狗','猴儿','大占','豆儿'],'辈分':'小弟'},index=[5,6,8,9]) print(df2) ''' 年龄 战斗力 昵称 辈分 18 90 老狗 小弟 18 89 猴儿 小弟 18 98 大占 小弟 18 100 豆儿 小弟 ''' 创建dataframe时建立索引
数据读取时创建索引列
import pandas as pd pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('univ.csv',encoding='gbk',index_col=['类型','学校名称']) #index_col=[指定索引列] print(df1)
2.设置某列为索引
import pandas as pd pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('univ.csv',encoding='gbk') df2=df1.set_index(keys=['名次','学校名称','总分'],drop=True,append=False,inplace=False) #设置['名次','学校名称','总分']联合索引,修改源文件 print(df2) ''' keys: 设置索引的列 drop=True: 建立索引之后是否删除该列? append=False: 是否在原流水索引的基础上添加索引,False默认是直接替换; inplace=False:是否直接替换原数据 '''
3.还原索引为普通列
import pandas as pd pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('univ.csv',encoding='gbk') df2=df1.set_index(keys=['名次','学校名称','总分'],drop=True,append=False,inplace=False) #设置['名次','学校名称','总分']联合索引,修改源文件 print(df2) df2.reset_index(level=['学校名称','总分','名次'],drop=False,inplace=True) #还原['名次','学校名称','总分']联合索引,源文件生效 print(df2) ''' drop=True: True将该索引直接删除?False还原为普通列! inplace=False:是否直接修改原数据 level=[] :指定还原的索引列 '''
4.修改索引
修改索引名称
import pandas as pd pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('univ.csv',encoding='gbk') df2=df1.set_index(keys=['名次','学校名称','总分'],drop=True,append=False,inplace=False) print(df2.index.names) df2.index.names=[None,None,None] #修改索引名称 print(df2.index.names)
修改索引值
索引值可以全部替换,但不能修改。
import pandas as pd pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('univ.csv',encoding='gbk') df2=df1.set_index(keys=['名次','学校名称','总分'],drop=True,append=False,inplace=False) df2.index=['a','b','c','d'] #修改索引值(必须一次性全部替换)
5.强行更新索引
通过df1.reindex() ,不加任何参数的方式还原所有索引列,无法清除 index_col=[ ]设置的索引;
import pandas as pd pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('univ.csv',encoding='gbk') #index_col=[指定索引列] print(df1.set_index(['名次'])) print(df1.reindex()) #还原所有索引列(还原不了读取 数据设置的 cl)
通过df1.reindex()填充缺失值
import pandas as pd pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('univ.csv',encoding='gbk') #index_col=[指定索引列] df2=df1.reindex([6,8,66,88,99,999999],fill_value='不知道') # 重置索引 [6,8,66,88,99,999999],取得原来索引对应的数据填入新表,999999号索引在原数据中没有对应的数据,所以需要做填充操作; print(df2) ''' 名次 学校名称 总分 类型 所在省份 所在城市 办学方向 主管部门 6 7.0 上海交通大学 81.76 综合 上海 上海市 中国研究型 教育部 8 9.0 国防科学技术大学 80.31 理工 湖南 长沙市 中国研究型 中央军委 66 67.0 合肥工业大学 64.88 理工 安徽 合肥市 行业特色研究型 教育部 88 89.0 中央民族大学 63.78 民族 北京 北京市 行业特色研究型 国家民委 99 100.0 山西大学 63.29 综合 山西 太原市 区域研究型 山西省 999999 NaN NaN NaN NaN NaN NaN NaN NaN ''' #显然 999999 '''' lables:[ ](列表结构的数值) copy=True:建立新对象而不是直接修改原数据 method:针对已经排序过的的索引 进行缺失值填充 method=pad / ffill: 用前面的有效数值填充 method=backfill /bfill: 用后面的有效数值填充 method=nearest: 使用最近的数值进行填充 fill_value='不知道' limit=None 设置向前、向后最大步长 '''
6.iloc和loc的区别
iloc根据默认的Index列进行索引
print(df.iloc[:, 0:2]) # [控制获取x:y行,控制获取x:y列],获取df中所有行,第0-2列。
loc根据手动设置的索引列进行索引
六、DataFrame行排序和筛选
1、排序
1.0. 根据索引排序:为什么要设置索引就是 便于快速排序、查询!
import pandas as pd pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('univ.csv',encoding='gbk') #index_col=[指定索引列] df1.set_index(['类型','学校名称'],inplace=True) #建立二重索引 df1.sort_index(level=['类型','学校名称'],ascending=[True,False],na_position='last',inplace=True) ''' level:多重索引时,用于排序的级别号,和名称 ascending=[True,False] 指定升序?降序?多列时以列表形式提供 na_position='last/first' 缺失值放在最前还是最后 inplace=True 修改原数据框 ''' print(df1)
1.1.根据变量值(列名称)进行排序:如果没有设置索引,也需要对数据进行排序,那么只能使用变量值进行排序了,虽然相比索引排序会较慢;
import pandas as pd pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('univ.csv',encoding='gbk') #index_col=[指定索引列] df1.sort_values(by=['类型','学校名称'],axis=0,ascending=[True,False],na_position='last',inplace=True) ''' by: 多重索引时,用于排序的变量名称 ascending=[True,False] 是否为升序排序? na_position='last/first' 设置缺失值 排列的顺序 最前 还是最后 inplace=True 是否对原数据框进行修改? ''' print(df1)
2.筛选
Pandas所有筛选都是基于 对行、列 设置 True/ False 值,True的是命中的、False的是为未命中的不显示的;
import pandas as pd pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('univ.csv',encoding='gbk') setlist=[True,False,True,True,False,True] print(df1.iloc[setlist]) ''' 名次 学校名称 总分 类型 所在省份 所在城市 办学方向 主管部门 0 1 北京大学 100.00 综合 北京 北京市 中国研究型 教育部 True 2 3 复旦大学 82.79 综合 上海 上海市 中国研究型 教育部 True 3 4 武汉大学 82.43 综合 湖北 武汉市 中国研究型 教育部 True 5 6 中国人民大学 81.98 综合 北京 北京市 中国研究型 教育部 True '''
2.0. 绝对位置筛选:按照行、列的绝对位置进行筛选
import pandas as pd pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('univ.csv',encoding='gbk') #iloc 即integer location 按照行、列序号进行检索,支持同时指定行列,指定列时需要先用","表名列序号 print(df1.iloc[0:3]) #筛选前3行 print(df1.iloc[[0,3]]) #筛选第 0行 和第3行 print(df1.iloc[:,0:3]) #筛选所有行(:)的的 第0 - 3列 print(df1.iloc[[1,4],0:3]) #筛选第 1、4行 的 第0 - 3列 print(df1.iloc[ [1,4],[0,3] ]) #筛选第 1、4行 的 第0、3列 print(df1.iloc[ 1:4,[0,3] ]) #筛选第 1到4行 的 第0、3列
2.1.按照索引值进行筛选
df.loc
根据索引位置进行切片操作
import pandas as pd pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('univ.csv',encoding='gbk') print(df1.loc[2:4]) # 设置名次列为索引列,获取索引为2 -- 索引为4 的行
import pandas as pd pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('univ.csv',encoding='gbk') df1.set_index(keys=['学校名称'],inplace=True) print(df1.loc['北京大学':'复旦大学']) #以 学校名称列为索引,获取 从北京大学-----复旦大学的行
根据索引筛选指定行
import pandas as pd pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('univ.csv',encoding='gbk') df1.set_index(keys=['学校名称'],inplace=True) print(df1.loc[['北京大学','清华大学',]]) #以 学校名称列为索引,仅获取 [北京大学、清华大学] 行
根据索引筛选指定的行 和 列
import pandas as pd pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('univ.csv',encoding='gbk') df1.set_index(keys=['学校名称'],inplace=True) print(df1.loc[['北京大学','清华大学',],['名次','所在省份']]) #以 学校名称列为索引,仅获取 [北京大学、清华大学]行,仅获取【名次','所在省份】列
2.2 多重索引检索
For example 现有中国大学名单,设置 “所在省份“,“学校名称”2列为多重索引;
利用多重索引进行检索,场景有3:
场景1: 仅使用1级索引
pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('univ.csv',encoding='gbk',index_col=['所在省份','学校名称']) # 设置多重索引 index_col=【所在省份、学校名称】 print(df1.loc[['河南','河北']]) #获取在河北、河南省的大学
场景2: 使用 下级(2...)级索引
import pandas as pd pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('univ.csv',encoding='gbk',index_col=['所在省份','学校名称']) # 设置多重索引 index_col=【所在省份、学校名称】 print(df1.xs(key='郑州大学',level=1,drop_level=False)) #利用二级索引学校名称 筛选出郑州大学 ''' 名次 总分 类型 所在城市 办学方向 主管部门 所在省份 学校名称 (多重索引) 河南 郑州大学 62 65.01 综合 郑州市 区域研究型 河南省 '''
场景3. 多重索引全部TM给我利用上
import pandas as pd pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('univ.csv',encoding='gbk',index_col=['所在省份','学校名称']) # 设置多重索引 index_col=【所在省份、学校名称】 # print(df1.loc[['河南','河北']]) #获取在河北、河南省的大学 print(df1.loc[[('河南','解放军信息工程大学'),('河北','燕山大学')]]) #利用多重索引完整检索('所在省份','学校名称)筛选出 河南的解放军信息工程大学 河北的 燕山大学
2.3 条件筛选
筛选出 中国排名前10的大小
import pandas as pd pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('univ.csv',encoding='gbk') # 设置多重索引 index_col=【所在省份、学校名称】 print( df1[df1.名次 < 11]) #筛选出 中国排名前10的大小
2.4 列表筛选
import pandas as pd pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('univ.csv',encoding='gbk') # 设置多重索引 index_col=【所在省份、学校名称】 print(df1[df1.名次.isin([1,5,6,8])]) #筛选 名次为 1,5,6,8 的行 print(df1[df1.所在省份.isin(['河北','河南'])]) #筛选 所在省份为 '河北','河南' 的行
索引列列表筛选
import pandas as pd pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('univ.csv',encoding='gbk',index_col='学校名称') # 设置 学校名称 为索引列 print(df1[df1.index.isin(['郑州大学'])]) #根据索引筛选 郑州大学
2.5 多重条件联合筛选
如果单一的条件筛选无法满足筛选需求,可以把多重条件联合在一起进行筛选,类似Django ORM里面的 Q 查询。
import pandas as pd pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('univ.csv',encoding='gbk',index_col='学校名称') # 设置 学校名称 为索引列 df2=df1[df1.名次 > 80 ] print(df2[df2.所在省份.isin(['河北','河南'])]) print('\033[1;35m ----------------------------花式一些------------------------------------\033[0m') df3=df1[df1.名次 > 80 ] [df1[df1.名次 > 80 ].所在省份.isin(['河北','河南'])] print(df3)
2.6 Python条件表达式筛选
联合条件筛选,要么一步步得引用变量,要么级联使用又容易导致语法错误;
Pandas有1中更加简单、容易的筛选方式 通过Python的条件表达式筛选;
import pandas as pd pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('univ.csv',encoding='gbk',index_col='学校名称') # 设置 学校名称 为索引列 capital='北京' city='上海' df2=df1.query('名次 > 10 and 名次 < 90 and 所在省份 not in [@capital,@city ]',inplace=False) print(df2)
七、DataFrame列操作
1.新增列
1.0.生成新列
import pandas as pd import numpy,math pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('univ.csv',encoding='gbk',index_col='学校名称') # 设置 学校名称 为索引列 df1['new_row']=df1.总分+df1.名次+1 #对列进行操作 新增1列 df1.new_row=numpy.sqrt(df1.new_row) #求新增列的平方根 df1.new_row=numpy.sqrt(df1.new_row) #求新增列的平方根 df1.new_row=df1.总分.apply(math.sqrt)+10 #使用math模块计算平方根+10 apply
1.1.对单列进行函数运算 apply(函数)
#apply( 函数表达式):对单列进行函数运行 def m_head(tmpstr): return tmpstr[:1] df1.new_row=df1.所在省份.apply(m_head)
from sqlalchemy import create_engine import matplotlib.pyplot as plt import matplotlib as mpl import datetime,time import pandas as pd mpl.use('Agg') mpl.rcParams['font.sans-serif'] = ['simhei'] mpl.rcParams['font.serif'] = ['simhei'] import seaborn as sns sns.set_style("darkgrid",{"font.sans-serif":['simhei','Droid Sans Fallback']}) # pd.options.display.max_rows = 10 # 配置最多输出多少行 def later_work_pie(): mysql_con=create_engine('mysql+pymysql://webproject:xxxxxx1234@172.17.10.112:3306/web') data=pd.read_sql(sql='cmdb_worker_order',con=mysql_con,columns=['alarm_time','initiator','fault_category','is_late','agent']).query('fault_category in [1,2,3,4] and is_late == True',inplace=False) def Unified_time_format(time_str): time_str_list=time_str.split(' ') if len(time_str_list) != 2: #2018.04.08 13.29.39 统一格式 2018.04.08_14:17:00 n=time_str.replace('_', ' ').replace(':', '.') return n else: return time_str data['alarm_time']=data['alarm_time'].apply(Unified_time_format) df1 = data.set_index(pd.to_datetime(data['alarm_time'], format='%Y.%m.%d %H.%M.%S')) last_monday=(datetime.datetime.now() +datetime.timedelta(days=-7)).strftime("%Y-%m-%d %H:%M:%S") this_monday=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") df1=df1[last_monday:this_monday] df=pd.value_counts(df1.agent) fig = plt.figure() #在任何绘图之前,我们需要一个Figure对象,可以理解成我们需要一张画板才能开始绘图。 ax = fig.add_subplot(111) #添加每一个 Axes可以理解成为真正可以作画的纸。 ax.set_title('本周超时工单信息') # print(dict(zip(df.index,df))) lables=[ i+' 数量:'+str(v) for i,v in dict(zip(df.index,df)).items() ] ax.pie(df,labels=lables,shadow = False,autopct ='%3.1f%%') plt.axis('equal') plt.savefig('/WorkOrderSystemData/plotings/late_work_order.png') #保存为图片
1.2.对多列进行函数运算 applymap(函数表达式)
#applymap(函数表达式):对多列进行相同的函数运算 def rows_add(row1): return row1+1000 print(df1[['总分','名次']].applymap(rows_add))
1.3.在指定位置 插入新的列
#在指定位置 插入新的列,zhuy df2.insert(1,'nwe',df1.总分.apply(rows_add)) print(df2)
1.4.df.assign 不修改原 DataFreme,而是生成新的df
#df.assign 不修改原 DataFreme,而是生成新的df df2=df1.assign(new=df1.总分.apply(rows_add))
import pandas as pd import numpy,math pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('univ.csv',encoding='gbk',index_col='学校名称') # 设置 学校名称 为索引列 df1['new_row']=df1.总分+df1.名次+1 #对列进行操作 新增1列 df1.new_row=numpy.sqrt(df1.new_row) #求新增列的平方根 df1.new_row=numpy.sqrt(df1.new_row) #求新增列的平方根 df1.new_row=df1.总分.apply(math.sqrt)+10 #使用math模块计算平方根+10 apply #apply( 函数表达式):对单列进行函数运行 def m_head(tmpstr): return tmpstr[:1] df1.new_row=df1.所在省份.apply(m_head) #applymap(函数表达式):对多列进行相同的函数运算 def rows_add(row1): return row1+1000 print(df1[['总分','名次']].applymap(rows_add)) #df.assign 不修改原 DataFreme,而是生成新的df df2=df1.assign(new=df1.总分.apply(rows_add)) #在指定位置 插入新的列,zhuy df2.insert(1,'nwe',df1.总分.apply(rows_add)) print(df2)
1.15.在最后的位置插入新的列
successful_df.insert(len(successful_df.columns),'probability',[ successful_df_count/total_count for i in range(len(successful_df))])
2.列中内容替换
2.1.对应数值的替换
import pandas as pd import numpy,math pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('univ.csv',encoding='gbk') # 设置 学校名称 为索引列 #精确替换单个值 df1.loc[1,'所在城市']='铁岭' #索引为3的行的 所在所在城市列 替换为唐县 #批量替换单个值 print(df1.replace('北京市','帝都',inplace=False)) #列表形式批量替换 多个值 print(df1.replace(['北京市','上海市'],['帝都','魔都'],inplace=False)) #字典形式 print(df1.replace({'北京市':"帝都",'上海市':"魔都"},inplace=False))
2.2.指定范围替换
import pandas as pd import numpy,math,re pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('univ.csv',encoding='gbk') # 设置 学校名称 为索引列 df2=df1.replace(regex=r'.*大学',value='野鸡大学') #正则表达式替换
import pandas as pd import numpy,math,re pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('univ.csv',encoding='gbk') # 设置 学校名称 为索引列 df2=df1.replace(regex=r'.*大学',value='野鸡大学') #正则表达式替换 print(df2) df1.loc[0:2,'总分']=250 #将前两行 总分列替换为250 # print(df1) df1.loc[df1.名次 < 10,'总分']=20 #将前10行的 总分列替换为20 # print(df1.head(10)) df1.loc[df1.query('名次 < 10 and 类型 == "综合"').index,'总分']=110 #将排名前10的类型等于综总分修改为110 # print(df1)
2.3.哑变量转换
虚拟变量(dummy variable),有的书上也称哑变量、假变量。我个人更喜欢虚拟变量这个称谓。因为“虚拟”这个词直接表达出了这个变量的含义。 虚拟,是与真实相对的,表示这个变量不是一个真实的变量。比如性别,用0和1表示,这里的0和1不是个真实的数值,而只是男和女的代码。我可以用0 和1表示,也可以用1和2表示,还可以用4和8表示,无论什么数值都可以表示,只不过我们习惯于用0和1表示而已。他们的差值也没有任何实际意义,不能说男和女相差1-0=1。0和1仅仅是个代码,这就是虚拟的含义。 现实中,虚拟变量一般用于多分类变量的很多情况下。比如,如果自变量是血型,分A、B、O、AB型。如果...
import pandas as pd import numpy,math,re pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('univ.csv',encoding='gbk') # 设置 学校名称 为索引列 # print(pd.get_dummies(df1.类型,prefix='pre')) print(pd.get_dummies(df1,columns=['类型'])) ''' data:希望转换的数据框、变量列 prefix:哑变量名称的前缀 prefix_sep:前缀和变量名称之间的分隔符 '''
2.4.数值分段
类似于对学生成绩做 优良中差划分
import pandas as pd import numpy,math,re pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('univ.csv',encoding='gbk') # 设置 学校名称 为索引列 df1['cls']=pd.cut(df1.名次,bins=[1,3,7],right=False) #数据值变量分段 ''' 名次 学校名称 总分 类型 所在省份 所在城市 办学方向 主管部门 cls 0 1 北京大学 100.00 综合 北京 北京市 中国研究型 教育部 [1, 3) 1 2 清华大学 98.50 理工 北京 北京市 中国研究型 教育部 [1, 3) 2 3 复旦大学 82.79 综合 上海 上海市 中国研究型 教育部 [3, 7) 3 4 武汉大学 82.43 综合 湖北 武汉市 中国研究型 教育部 [3, 7) 4 5 浙江大学 82.38 综合 浙江 杭州市 中国研究型 教育部 [3, 7) ''' print(df1.head(10))
八、DataFrame 数据框架管理
前面是对DataFrame的导入、索引、行、列的操作,下面主要讲述的是对 DataFrame数据的拆分、分组、汇总、结构调整、连表操作;
1.分组(groupby)和聚合(agg)
在SQL查询中我们有分组和聚合操作,在pandas中也是。
分组、汇总如图所示 就是 先分组(拆分),然后运用对每组数据做聚合操作( max、min、sum、mean....)
Split--->Apply--->Combine
1.0.分组(groupby)
注意:如果上下2行相同就会合并为1条记录,所以groupby有去重的效果;
# #说明、公共属性、自定义事件表=1个事件表 df = pd.read_excel(io="pandas.xlsx",header=None) df.columns = ['name','gender','age']#赋予变量给列 df1=df.groupby(['name'])['age'].unique() for value in df1.index.values: # if len(df1[value]) > 1: print(df1[value])
import pandas as pd import numpy,math,re pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('a.csv',sep='|') # 设置 个体ID 、年龄 为索引列 print(df1.groupby('名称').groups) # .groupby进行分组,.groups查看分组情况 print(df1.groupby('名称').describe()) #分析 各组的每个数值列 的计数,平均值,标准差,最小值,最大值以及较低的百分位。 print(df1.groupby(['名称','ID']).mean()) #对 数据进行 多重分组,并对每个求出 所有数字列的均值 print(df1.groupby(['类型']).get_group('农林').mean()) #获取其中的一组,求均值 print(df1.groupby('名称')['排名'].min()) #按名称分组,求每组中 排名列 的 最小值 print(df1.groupby('名称')['排名'].mean()) #按名称分组,求每组中 排名列 的 均值 print(df1.groupby('名称')['排名'].max()) #按名称分组,求每组中 排名列 的 最大值
1.1.汇总(aggregation)
agg是一个聚合函数,聚合函数操作始终是在轴,默认是列轴,也可设置行轴上执行。
import pandas as pd import numpy,math,re pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_csv('a.csv',sep='|') # 设置 个体ID 、年龄 为索引列 df2=df1.groupby('名称') #1.split 分组 def mymean(x): return x.mean() print(df2.agg('count')) #求每组的个数 print(df2.agg(['mean','median' ])) #求均数、中位数(涉及所有数值列) df2.agg(mymean) #自定义分组函数
---------------------
import pandas as pd df=pd.DataFrame([ [1,2,3,4], [11,22,33,44], [111,222,333,444], [1111,2222,3333,4444] ], columns=['col1','col2','col3','col4'],)#列名 print(df) # df=df.agg(['max','min','mean']) # print(df) df_columns=df.agg({'col1':['sum','min'],'col2':['max','min'],'col3':['sum','min']}) print(df_columns)
1.2.交叉表和透视表
透视表和交叉表还是对数据进行分组和聚合分析。
a.交叉表(crosstab)
交叉表可以反映2个列之间的关系,操作就是把2列数据交叉成1个直角关系。
data_df.drop_duplicates(inplace=True) print(pd.crosstab(data_df["权利人名称"],data_df["建房年代"]))
效果
建房年代 1949 1952 1955 1960 1963 ... 2006 2007 2008 2009 1982、1997 权利人名称 ... 张志隔 0 0 0 0 0 ... 0 0 0 0 0 丁久旦 0 0 0 0 0 ... 0 0 0 0 0 张卫强 0 0 0 0 0 ... 0 1 0 0 0 丁大全 0 0 0 0 0 ... 0 0 0 0 0 丁三亮 1 0 0 0 0 ... 0 0 0 0 0 ... ... ... ... ... ... ... ... ... ... ... ... 齐建好 0 0 0 0 0 ... 0 0 0 0 0 齐小中 0 0 0 0 0 ... 0 0 0 0 0 小杰子 0 0 0 0 0 ... 0 0 0 0 0 老玉振 1 0 0 0 0 ... 0 0 0 0 0 小芬子 0 0 0 0 0 ... 0 0 0 0 0
b.透视表(pivot_table)
透视表本质还是对数据分组之后,进行聚合操作。
index: 根据什么列进行分组
values:对那些列进行聚合操作
aggfunc:进行何种聚合操作?mean?max?min?sum?...
data_df.drop_duplicates(inplace=True) df = pd.pivot_table(data_df, index=["实际用途"], values=["占地面积 (㎡)", "建筑面积(㎡)"], aggfunc=["mean", "max", "min"]).round( 2)
效果
mean max min
占地面积 (㎡) 建筑面积(㎡) 占地面积 (㎡) 建筑面积(㎡) 占地面积 (㎡) 建筑面积(㎡)
实际用途
宅基地 387.0 180.11 1087.44 507.33 23.69 16.93
2.长形格式和宽形格式相互转换
2.0.什么是长形格式和宽形格式?
长形格式:每1个个体的 每1次测量都会被单独 记录为1个 Case(行),有几次测量就会有几行!
特征:同1个个体的信息会重复出现
宽形格式:每个个体被记录为1个case,不同的测量指标被记录为不同的变量
特征:1个个体的信息不会重复出现
2.1.长形和宽形格式如何相互转换?
import pandas as pd import numpy,math,re pd.options.display.max_rows=10 #配置最多输出多少行 df1=pd.read_excel('children.xlsx',index_col=[0,2]) #Pandas基于多重索引,做长形和宽形相互转换 df2=df1.stack() #转换成最简的 多重索引模式 #print(df2.unstack(level=1)) # level=你要unstack 的索引级别(也就是把那列 进行分组 展开变成列) # print(df2.unstack([1,2])) #把 level 1的年龄列和 性别 测量值都展开变成列 # print(df1.T) #数据转置(列变行)
3.多数据源的合并
如何把2个或者多个 Excel文件、CSV文件在Pandas中合并起来呢?读取了数据库中的2中有外键关系的表如何进行连表呢?
3.0.纵向合并
纵向合并就是把 用有相同列属性 的数据上下合并在一起;
import pandas as pd import numpy,math,re from sqlalchemy import create_engine import matplotlib.pyplot as plt import matplotlib as mpl mpl.rcParams['font.sans-serif'] = ['KaiTi'] mpl.rcParams['font.serif'] = ['KaiTi'] pd.options.display.max_rows=50 #配置最多输出多少行 df1=pd.read_csv('a.csv',sep='|') df2=pd.read_csv('b.csv',sep='|') # print(df1.append(other=df2,ignore_index=True,verify_integrity=True)) #other:合并连接的对象 ignore_index:忽略原来的索引 verify_integrity 验证索引的错误报错 print(df1.append(other=[df2[:10],df2[10:]],ignore_index=True,verify_integrity=True)) #合并多个数据源 使用列表[ 传参];
import pandas as pd bj08=pd.read_csv(filepath_or_buffer='PM25\Beijing_2008_HourlyPM2.5_created20140325.csv',header=2,usecols=[0,2,3,4,5,6,7,9,10]) bj09=pd.read_csv(filepath_or_buffer='PM25\Beijing_2009_HourlyPM25_created20140709.csv',header=2,usecols=[0,2,3,4,5,6,7,9,10]) bj10=pd.read_csv(filepath_or_buffer='PM25\Beijing_2010_HourlyPM25_created20140709.csv',header=2,usecols=[0,2,3,4,5,6,7,9,10]) bj11=pd.read_csv(filepath_or_buffer='PM25\Beijing_2011_HourlyPM25_created20140709.csv',header=2,usecols=[0,2,3,4,5,6,7,9,10]) bj12=pd.read_csv(filepath_or_buffer='PM25\Beijing_2012_HourlyPM2.5_created20140325.csv',header=2,usecols=[0,2,3,4,5,6,7,9,10]) bj13=pd.read_csv(filepath_or_buffer='PM25\Beijing_2013_HourlyPM2.5_created20140325.csv',header=2,usecols=[0,2,3,4,5,6,7,9,10]) bj14=pd.read_csv(filepath_or_buffer='PM25\Beijing_2014_HourlyPM25_created20150203.csv',header=2,usecols=[0,2,3,4,5,6,7,9,10]) bj15=pd.read_csv(filepath_or_buffer='PM25\Beijing_2015_HourlyPM25_created20160201.csv',header=2,usecols=[0,2,3,4,5,6,7,9,10]) bj16=pd.read_csv(filepath_or_buffer='PM25\Beijing_2016_HourlyPM25_created20170201.csv',header=2,usecols=[0,2,3,4,5,6,7,9,10]) bj=bj08.append(other=[bj09,bj10,bj11,bj12,bj13,bj14,bj15,bj16],ignore_index=True,verify_integrity=True) print(bj.to_html())
3.1 横向合并
横向合并 类似 MySQL中的join连表操作,在Pandas中使用pd.merge、join、concat 方法均可实现,Actually 吾生有崖,学无涯,So I only studied meger method of Pandas, 以下是merge()接受的参数;
left: 拼接的左侧DataFrame对象 right: 拼接的右侧DataFrame对象 how:left/right/inner(和MySQL的SQL连表规则是一样一样的) let: 左侧连接(显示左侧表全部数据,如果右侧表没有对应的数据,NAN代替) right:右侧连接(显示右侧表全部数据,如果左侧表没有对应的数据,NAN代替) inner:等值连接(公平 只显示有关联的数据) left_index=True/False: 使用左侧表的索引去连接 right_index=True/Fales: 使用右侧表的索引去连接 sort=True:是否排序 indicator:新增1列 标记数据来源 right_only/left_only/both
import numpy,math,re from sqlalchemy import create_engine import matplotlib.pyplot as plt import matplotlib as mpl mpl.rcParams['font.sans-serif'] = ['KaiTi'] mpl.rcParams['font.serif'] = ['KaiTi'] pd.options.display.max_rows=50 #配置最多输出多少行 df1=pd.read_csv('a.csv',sep='|') df2=pd.read_csv('b.csv',sep='|') df3=pd.read_csv('c.csv',sep='|') df4=df1.append(df2) df5=pd.merge(df3,df4) print(df5)
import pandas as pd from sqlalchemy import create_engine pd.options.display.max_rows=20 #配置最多输出多少行 mysql_con=create_engine('mysql+pymysql://webproject:xxxxxx1234@172.17.10.112:3306/web?charset=utf8') df1=pd.read_sql(sql='cmdb_host',con=mysql_con,columns=['hostname','ip_addr','port','idc_id'])[:20] df2=pd.read_sql(sql='cmdb_idc',con=mysql_con,columns=['id','name'])[:20] # print(df1) # print(df2) print(pd.merge(left=df1,right=df2,how='right',left_on='idc_id',right_on='id',sort=True,indicator='来源标记')) ''' left: 拼接的左侧DataFrame对象 right: 拼接的右侧DataFrame对象 how:left/right/inner let: 左侧连接(显示左侧表全部数据,如果右侧表没有对应的数据,NAN代替) right:右侧连接(显示右侧表全部数据,如果左侧表没有对应的数据,NAN代替) inner:等值连接(公平 只显示有关联的数据) left_index=True/False: 使用左侧表的索引去连接 right_index=True/Fales: 使用右侧表的索引去连接 sort=True:是否排序 indicator:新增1列 标记数据来源 right_only/left_only/both '''
九、Pandas数据清洗
以上内容介绍了Panda如何读取数据源、建立索引,如何修改、筛选行和列,如何合并、连表多个数据源,数据准备好了,接下来就是数据清洗工作了。
1.设置缺失值
如果 DataFrame中 某些数据 是不需要参与计算、分析的,我们可以把他们设置为系统缺失值;
在DataFrame中默认有2种缺失值 None/np.nan,使用df.isnan()进行 True/False判断。
1.0.系统缺失True/False判断、检查
a.检查每个单元格是否为缺失值?
import pandas as pd import numpy df1=pd.read_csv(filepath_or_buffer='univ.csv',encoding='gbk') print(df1.replace(to_replace=['北京',100],value=numpy.nan).isna()) #检查每个单元格是否为缺失值?
b.检查 指定列 中是否包含缺失值?
import pandas as pd import numpy df1=pd.read_csv(filepath_or_buffer='univ.csv',encoding='gbk') # print(df1.replace(to_replace=['北京',100],value=numpy.nan).isna()) #检查每个单元格是否为缺失值? print(df1.replace(to_replace=['北京',100],value=numpy.nan).名次.isna()) #检查指定列是否包含缺失?
c.检查每行是否包含缺失值?
import pandas as pd import numpy df1=pd.read_csv(filepath_or_buffer='univ.csv',encoding='gbk') # print(df1.replace(to_replace=['北京',100],value=numpy.nan).isna()) #检查每个单元格是否为缺失值? # print(df1.replace(to_replace=['北京',100],value=numpy.nan).名次.isna()) #检查指定列是否包含缺失? print(df1.replace(to_replace=['北京',100],value=numpy.nan).isna().any(1)) #检查每行是否包含缺失值?
d.把包含缺失值的行 显示出来
import pandas as pd import numpy df1=pd.read_csv(filepath_or_buffer='univ.csv',encoding='gbk') # print(df1.replace(to_replace=['北京',100],value=numpy.nan).isna()) #检查每个单元格是否为缺失值? # print(df1.replace(to_replace=['北京',100],value=numpy.nan).名次.isna()) #检查指定列是否包含缺失? # print(df1.replace(to_replace=['北京',100],value=numpy.nan).isna().any(1)) #检查每行是否包含缺失值? print(df1[df1.replace(to_replace=['北京',100],value=numpy.nan).isna().any(1)]) #把包含缺失值的行 显示出来
1.1.缺失值设置为None/numpy.nan的区别
import pandas as pd import numpy df1=pd.read_csv(filepath_or_buffer='univ.csv',encoding='gbk') print(None==None) #True print(numpy.nan == numpy.nan) #False 不可以和自身进行比较
import pandas as pd import numpy df1=pd.read_csv(filepath_or_buffer='univ.csv',encoding='gbk') df2=df1.replace(to_replace=['北京'],value=[numpy.nan]) #会把所有的 "北京" 完全替换成 NaN print(df1.所在省份.replace(to_replace=['北京'],value=None))#如果把缺失值设置为None时,使用左右、原来其他值代替; ''' 0 北京 1 北京 2 上海 3 湖北 '''
PS.把缺失值设置为Nan
2.处理缺失值
对于缺失值的处理方法无非2种,替换或者删除,;
2.0.替换成指定数字、字符串fillna()
import pandas as pd import numpy df1=pd.read_csv(filepath_or_buffer='univ.csv',encoding='gbk') # print(df1.replace(to_replace=['北京',100],value=numpy.nan).isna()) #检查每个单元格是否为缺失值? # print(df1.replace(to_replace=['北京',100],value=numpy.nan).名次.isna()) #检查指定列是否包含缺失? # print(df1.replace(to_replace=['北京',100],value=numpy.nan).isna().any(1)) #检查每行是否包含缺失值? # print(df1[df1.replace(to_replace=['北京',100],value=numpy.nan).isna().any(1)]) #把包含缺失值的行 显示出来 df2=df1.replace(to_replace=['北京',100],value=numpy.nan).fillna('未知') #把缺失值替换成 指定字符串 df3=df1.replace(to_replace=['北京',100,1,2,3],value=numpy.nan).fillna(df1.mean()) #把缺失值替换成 中位数(如果当前列的数据类型为数字 否则仍然是nan)
根据邻近得值进行填充
new_part_pd=pd.concat([part_df,public_attr,],axis=0,sort=True,join='outer').fillna(method='pad',axis=0)##按照列(横向填充)NaN上1和
method: 根据什么规则进行填充? pad/ffill:用前一个非缺失值去填充该缺失值,用左边的非缺失值去填充该缺失值 backfill/bfill:用下一个非缺失值填充该缺失值,用右边的非缺失值去填充该缺失值 None:指定一个值去替换缺失值(默认这种方式) limit参数:限制填充个数 axis参数:修改填充方向,默认为纵向填充,axis=1的时候,横向填充
2.1.删除缺失值 dropna()
''' axis:0 0代表行 1代表列 how: any任何1个单元格出现NAN就删除,all 所有单元格都出现 NAN才删除 thresh: 设置删除行或者列的数量 subset:指定删除的行/列 '''
import pandas as pd import numpy df1=pd.read_csv(filepath_or_buffer='univ.csv',encoding='gbk') # print(df1.replace(to_replace=['北京',100],value=numpy.nan).isna()) #检查每个单元格是否为缺失值? # print(df1.replace(to_replace=['北京',100],value=numpy.nan).名次.isna()) #检查指定列是否包含缺失? # print(df1.replace(to_replace=['北京',100],value=numpy.nan).isna().any(1)) #检查每行是否包含缺失值? # print(df1[df1.replace(to_replace=['北京',100],value=numpy.nan).isna().any(1)]) #把包含缺失值的行 显示出来 # df2=df1.replace(to_replace=['北京',100],value=numpy.nan).fillna('未知') #把缺失值替换成 指定字符串 # df3=df1.replace(to_replace=['北京',100,1,2,3],value=numpy.nan).fillna(df1.mean()) #把缺失值替换成 中位数(如果当前列的数据类型为数字 否则仍然是nan) df4=df1.replace(to_replace=['北京',100],value=numpy.nan).dropna(axis=0,how='any',thresh=3,subset=['总分','所在省份','名次']) ''' axis:0 0代表行 1代表列 how: any任何1个单元格出现NAN就删除,all 所有单元格都出现 NAN才删除 thresh: 设置删除行或者列的数量 subset:指定删除的行/列 ''' print(df4)
3.数据查重 .duplicated()和重复行删除.drop_duplicates()
3.0. 新增 dup 列 标识出 重复行
PS.只有 第二次出现的才会被标识出来
import pandas as pd import numpy df1=pd.read_csv(filepath_or_buffer='univ.csv',encoding='gbk') df1['dup']=df1.duplicated(['类型','所在省份']) #标识出 '类型','所在省份'两列重复的行 print(df1)
3.1.检查索引是否重复并标识出来 index.duplicated()
import pandas as pd import numpy df1=pd.read_csv(filepath_or_buffer='univ.csv',encoding='gbk') # df1['dup']=df1.duplicated(['类型','所在省份']) #标识出 '类型','所在省份'两列重复的行 # print(df1) df1['dup']=df1.set_index(['类型','所在省份']).index.duplicated() print(df1)
print(sa_df.duplicated(subset=['SAevent_en','SAevent_cn', 'SAproperty_en', 'SAproperty_cn', 'SAproperty_data_type']).any())
3.2.删除出现重复的行 df1.drop_duplicates()
subset:设置重复的列或者索引列
keep:重复之后删除规则(first/last/False,first保留第1个,last保留最后1个,False全部删除)
import pandas as pd import numpy df1=pd.read_csv(filepath_or_buffer='univ.csv',encoding='gbk') # # df1['dup']=df1.duplicated(['类型','所在省份']) #标识出 '类型','所在省份'两列重复的行 # print(df1) # # df1['dup']=df1.set_index(['类型','所在省份']).index.duplicated() print(df1.drop_duplicates(subset=['类型','所在省份'],keep=False)) ''' subset:设置重复的列或者索引列 keep:重复之后删除规则(first/last/False,first保留第1个,last保留最后1个,False全部删除) ''' print(df1[~df1.duplicated(subset=['类型','所在省份'],keep=False)]) #简洁语法
3.3 df.empty 判断一个dataframe非空
if df_design.empty: logs.append('事件设计错误,请再检查一下事件设计!') return render(request, 'update_cname.html', {"logs": logs, "is_update": True})
十、处理 日期、时间数据
很多数据分析都是以时间轴为基础进行的,Maybe 小时的订单量、每天的订单量whatever....
所以我们需要把DataFreme中时间转换成Pandas理解的时间,然后建立时间索引,进行灵活、切边 再进行汇总分析;
Pandas中有4种时间类型
Timestamp: 表示具体的日期时间(精确到秒)
DatetimeIndex: 基于Timestamp建立日期时间索引
Period: Timestamp表示精确时间而Period可以表示1段时间 Forexample (2019年6月这1月 、2019年6月1号 这1天)
PeriodIndex: 如果基于Period时间段建立索引就是PeriodIndex
1.创建Timestamp和Period日期时间类型
import pandas as pd from datetime import datetime print(pd.Timestamp(datetime(1993,6,25))) #datetime become Timestamp print(pd.Period('1993-05',freq='D'))
2.将已有数据转成Timestamp时间类型(pd.to_datetime)
既然使用Pandas做数据分析,就得把您的日期时间数据转换成Pandas理解的时间类型
import pandas as pd from datetime import datetime bj08=pd.read_csv(filepath_or_buffer='PM25\Beijing_2008_HourlyPM2.5_created20140325.csv',header=2,usecols=[0,2,3,4,5,6,7,9,10]) print(pd.Timestamp(bj08['Date (LST)'][0])) #转换单个字符串 print(bj08['Date (LST)'].apply(pd.Timestamp)) #转成整行 #以上的日期时间转换方式如果不尽如你意,可以使用to_datatime() print(pd.to_datetime(datetime(1993,6,30,8,26))) #转换单个datetime日期 print(pd.to_datetime("1993-6-30 8:26")) #转换单个字符串日期 print(pd.to_datetime(['1993/6/30','1993.6.30'])) #转换列表日期 print(pd.to_datetime(bj08['Date (LST)'],format='%Y-%m-%d %H:%M')) #批量转换 print(pd.to_datetime(bj08[['Year','Month','Day','Hour']])) #将多列合并转换成 Timestamp时间类型
3.DatatimeIndex索引时间类型
把日期时间数据转成Timestamp时间类型后,面临以下几个问题;
如何根据时间的层级提取数据 ?(年/月/日/....)
如何快速提取指定时间的数据?
如何按照时间范围提取数据?(提取8-9月的订单)
所以最后需要建立DatatimeIndex时间索引;
import pandas as pd from datetime import datetime bj08=pd.read_csv(filepath_or_buffer='PM25\Beijing_2008_HourlyPM2.5_created20140325.csv',header=2,usecols=[0,2,3,4,5,6,7,9,10]) # print(pd.date_range('1/1/2012',periods=5,freq='M')) #生成 2012 1-5月的数据 5条记录 bj08idx=bj08.set_index(pd.to_datetime(bj08['Date (LST)'])) #转成Datetimestamp时间类型,然后建立DatetimeIndex时间索引 # print(type(bj08idx.index)) #<class 'pandas.core.indexes.datetimes.DatetimeIndex'> print(bj08idx['2008-11-1':'2008-11-5 9:00']) #基于DatetimeInde时间索引 切片 # print(bj08idx['2008-11-1']) #基于DatetimeIndex时间索引 获取某天的数据x
4.基于DatatimeIndex时间索引进行分组汇总
可以根据日期时间,灵活切割数据之后,就是对数据的分组、汇总、分析了;
resample()有很多参数,参考官网;
import pandas as pd from datetime import datetime bj08=pd.read_csv(filepath_or_buffer='PM25\Beijing_2008_HourlyPM2.5_created20140325.csv',header=2,usecols=[0,2,3,4,5,6,7,9,10]) bj08idx=bj08.set_index(pd.to_datetime(bj08['Date (LST)'])) #转成Datetimestamp时间类型,然后建立DatetimeIndex时间索引 # every_hour_data=bj08idx.index.hour #获取每个小时的数据 # print(bj08idx.groupby(every_hour_data).max()) #获取每个小时PM2.5的最大值 print(bj08idx.resample('3D').mean()) #按3天进行分组,然后求平均值 ''' 2008-04-08 15:00:00 2008.0 4.000000 9.625000 11.500000 87.361111 2008-04-11 15:00:00 2008.0 4.000000 12.625000 11.500000 78.236111 2008-04-14 15:00:00 2008.0 4.000000 15.625000 11.500000 186.083333 2008-04-17 15:00:00 2008.0 4.000000 18.625000 11.500000 131.555556 '''
5.对时间索引缺失值的处理
如果时间索引列 的时间数据是固定时间频率,某个时间值缺失也是可以填充的;
2009-02-01 00:00:00 Beijing 2009-02-17 17:00 2009.0 2.0 17.0 17.0 2009-02-01 01:00:00 Beijing 2009-02-17 17:00 2009.0 2.0 17.0 17.0 2009-02-01 NAN Beijing 2009-02-17 17:00 2009.0 2.0 17.0 17.0 2009-02-01 03:00:00 Beijing 2009-02-17 17:00 2009.0 2.0 17.0 17.0
步骤如下
1、筛选出 value值大于0的索引(筛选出有效数据)
2、利用date_range生成崭新的完整索引
3.删除缺失值 重新建立索引
import pandas as pd from datetime import datetime bj09=pd.read_csv(filepath_or_buffer='PM25\Beijing_2009_HourlyPM25_created20140709.csv',header=2,usecols=[0,2,3,4,5,6,7,9,10]) bj09idx=bj09.set_index(pd.to_datetime(bj09['Date (LST)'])) #1、筛选出 value值大于0的索引(筛选出有效数据) bj09idx=bj09idx[bj09idx.Value > 0] # print(bj09idx) #2、利用date_range生成崭新的完整索引 idx=pd.date_range(start='2009-02-1 00:00:00',end='2009-12-31 23:00:00',freq='H') # bj09idx.reindex(idx) #把新的索引给 bj09idx 建立新索引 #print(bj09idx[bj09idx.index.duplicated()]) #显示重复的索引,发现2个2009-03-08 03:00:00 索引 # 2009-03-08 03:00:00 1 Hr Valid #2009-03-08 03:00:00 1 Hr Valid # print(bj09idx['2009-03-08']) # #3.删除缺失值 重新建立索引 bj10idx10=bj09idx[~bj09idx.index.duplicated()].reindex(idx,method='bfill') #print(bj10idx10.shift(3)) #序列数值平移
十一、基于Pandas对数据绘图
import numpy,math,re from sqlalchemy import create_engine import matplotlib.pyplot as plt import matplotlib as mpl import pandas as pd mpl.rcParams['font.sans-serif'] = ['KaiTi'] mpl.rcParams['font.serif'] = ['KaiTi'] pd.options.display.max_rows=10 #配置最多输出多少行 mysql_con=create_engine('mysql+pymysql://webproject:xxxxxx1234@172.17.10.112:3306/web') oracle_conn=create_engine('oracle+cx_oracle://scott:tiger@172.16.22.16:1521/ecdb') data=pd.read_sql(sql='cmdb_worker_order',con=mysql_con,index_col='seach',columns=['agent','fault_category']).query('fault_category in [0,1,2,3]',inplace=False) pd.value_counts(data.agent).plot.barh() plt.title('运维平台用户2018年工单创建量') plt.xlabel('数量') plt.ylabel('用户') plt.savefig('1.png')#保存图片(把图片保存在服务器,就可以通过前端 src啦!) plt.show()
from sqlalchemy import create_engine import matplotlib.pyplot as plt import matplotlib as mpl import pandas as pd mpl.rcParams['font.sans-serif'] = ['KaiTi'] mpl.rcParams['font.serif'] = ['KaiTi'] pd.options.display.max_rows=10 #配置最多输出多少行 mysql_con=create_engine('mysql+pymysql://webproject:xxxxxx1234@172.17.10.112:3306/web') oracle_conn=create_engine('oracle+cx_oracle://scott:tiger@172.16.22.16:1521/ecdb') data=pd.read_sql(sql='cmdb_worker_order',con=mysql_con,index_col='seach',columns=['agent','fault_category']).query('fault_category in [0,1,2,3]',inplace=False) df=pd.value_counts(data.agent) fig = plt.figure() #在任何绘图之前,我们需要一个Figure对象,可以理解成我们需要一张画板才能开始绘图。 ax = fig.add_subplot(111) #添加每一个 Axes可以理解成为真正可以作画的纸。 ax.set_xlabel('员工姓名') #plt.gcf() #获取当前Figure/plt.gca()#获取当前Axes ax.set_ylabel('工单处理数量') ax.set_title('2018年员工工单处理数量') for a,b in zip(df.index,df.values): #plt.text(第一个参数是x轴坐标,第二个参数是y轴坐标,第三个参数是要显式具体数字 ) plt.text(a,b,b, ha='center', va='bottom', fontsize=19) ax.bar(df.index,df.values) #画bar条图 plt.show()
数据读入-->建立索引---->清洗完毕了------>千辛万苦得把数据整理完了,下面就是通过 matplotlib模块绘图,来了解一下数据的概况了;
2.df1.plot参数
df1.plot(x=df1.index, title='工单月创建量',kind='line',figsize=(25,15))
'''
x:x轴变量名称
y:y轴的变量名称
kind:指定要绘制的图形种类
line 线图
bar 垂直的条图
barh 水平的条图
hist 方图
density 密度图
box 箱图
area面积图
pie 饼图
label:对图的描述
figsize:指定图形的宽、高
xlim/ylim:X轴Y轴的取值范围
'''
from sqlalchemy import create_engine import pandas as pd import matplotlib.pyplot as plt #导入python绘图模块 import seaborn import matplotlib.dates as mdate plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['font.serif'] = ['KaiTi'] seaborn.set_style('whitegrid',{'font.sans-serif':['simhei','Arial']}) #seaborn模块美化绘图的模块 加上白色空格 mysql_con=create_engine('mysql+pymysql://webproject:xxxxxx1234@172.17.10.112:3306/web') data=pd.read_sql(sql='cmdb_worker_order',con=mysql_con,columns=['alarm_time','agent','fault_category']).query('fault_category not in [0,1,2,3]',inplace=False) df1=data.set_index(pd.to_datetime(data['alarm_time'],format='%Y.%m.%d_%H:%M:%S')).resample('M').agent.count() df1.plot(x=df1.index, title='工单月创建量',kind='line',figsize=(25,15)) plt.tick_params(axis="both",which="major",labelsize=8) plt.show() #plt.savefig('1.png')#保存图片(把图片保存在服务器,就可以通过前端 src啦!) ''' x:x轴变量名称 y:y轴的变量名称 kind:指定要绘制的图形种类 line 线图 bar 垂直的条图 barh 水平的条图 hist 方图 density 密度图 box 箱图 area面积图 pie 饼图 label:对图的描述 figsize:指定图形的宽、高 xlim/ylim:X轴Y轴的取值范围 '''
3.设置matplotlib x轴的时间刻度、显示格式 参考
import numpy,math,re from sqlalchemy import create_engine import pandas as pd import matplotlib.pyplot as plt #导入python绘图模块 import seaborn import matplotlib.dates as mdate from matplotlib.dates import DateFormatter,datestr2num,DateFormatter # plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['font.serif'] = ['KaiTi'] seaborn.set_style('whitegrid',{'font.sans-serif':['simhei','Arial']}) #seaborn模块美化绘图的模块 加上白色空格 mysql_con=create_engine('mysql+pymysql://webproject:xxxxxx1234@172.17.10.112:3306/web') data=pd.read_sql(sql='cmdb_worker_order',con=mysql_con,columns=['alarm_time','agent','fault_category']).query('fault_category not in [0,1,2,3]',inplace=False) df1=data.set_index(pd.to_datetime(data['alarm_time'],format='%Y.%m.%d_%H:%M:%S')). resample('M').count() fig = plt.figure() #在任何绘图之前,我们需要一个Figure对象,可以理解成我们需要一张画板才能开始绘图。 ax = fig.add_subplot(111) #添加每一个 Axes可以理解成为真正可以作画的纸。 formatter = DateFormatter('%m') #设置时间显示格式 为/月 ax.xaxis.set_major_locator(mdate.MonthLocator()) ax.xaxis.set_major_formatter(formatter) ax.set_xlabel('时间(月份)') #plt.gcf() #获取当前Figure/plt.gca()#获取当前Axes ax.set_ylabel('数量') ax.set_title('2018年工单创建情况') ax.plot(df1.index,df1['fault_category'],color='r') plt.show() # plt.savefig('3.png') #保存图片
4.plt.text()在图形中添加纯文本注释,显示具体数值。
plt.text(第一个参数是x轴坐标,第二个参数是y轴坐标,第三个参数是要显式具体数字 )
for a,b in zip(df.index,df.values): plt.text(a, b,b, ha='center', va='bottom', fontsize=9)
5.pie饼图
from sqlalchemy import create_engine import matplotlib.pyplot as plt import matplotlib as mpl import pandas as pd mpl.rcParams['font.sans-serif'] = ['KaiTi'] mpl.rcParams['font.serif'] = ['KaiTi'] pd.options.display.max_rows=10 #配置最多输出多少行 mysql_con=create_engine('mysql+pymysql://webproject:xxxxxx1234@172.17.10.112:3306/web') oracle_conn=create_engine('oracle+cx_oracle://scott:tiger@172.16.22.16:1521/ecdb') data=pd.read_sql(sql='cmdb_worker_order',con=mysql_con,columns=['initiator','fault_category','is_late','agent']).query('fault_category in [0,1,2,3,4] and is_late == True',inplace=False) df=pd.value_counts(data.agent) fig = plt.figure() #在任何绘图之前,我们需要一个Figure对象,可以理解成我们需要一张画板才能开始绘图。 ax = fig.add_subplot(111) #添加每一个 Axes可以理解成为真正可以作画的纸。 ax.set_title('当前超时工单信息') print(dict(zip(df.index,df))) lables=[ i+' 数量:'+str(v) for i,v in dict(zip(df.index,df)).items() ] ax.pie(df,labels=lables,shadow = False,autopct ='%3.1f%%') plt.axis('equal') plt.savefig('late_work_order.png') #保存为图片
以上是我对pandas0.20.0版本的使用,以下是我对pandas0.25.1版本的使用
行操作
获取df中的第X行数据
xls = pd.ExcelFile('pandas.xlsx') df = xls.parse(header=None) print(df.iloc[3]) ''' 0 小王 1 12 2 女 Name: 3, dtype: object '''
#如果是仅有1列的df可使用函数
df.columns=['name','age','gender']
df1=df.groupby(by='name').size().loc[lambda x: x == 1]
print(df['name'].loc[lambda x:x=='小丽'])
获取df的每1行数据以及索引
xls = pd.ExcelFile('pandas.xlsx') df = xls.parse( header=None) for index, item in df.iterrows(): print(item) ''' 0 姓名 1 年龄 2 性别 Name: 0, dtype: object 0 张根 1 18 2 男 Name: 1, dtype: object '''
精确获取第X行中的第X个数据
xls = pd.ExcelFile('pandas.xlsx') df = xls.parse(header=None) print(df.iloc[4,0])#位于第4行中的第0个元素
拆分1个大的单元格
for index, item in df.iterrows(): if index == 0: item[1] = '公共属性' item[2] = '公共属性' elif not pd.isnull(item[1]): pass else: item[1] = df.iloc[index - 1, 1].replace('\n','') item[2] = df.iloc[index - 1, 2] item[7] = df.iloc[index - 1, 7] item[8] = df.iloc[index - 1, 8]
使用df.iloc[:,:3]获取X列数据
print(df.iloc[:,:3]) #获取3列数据
获取1-最后1行数据(掐头)
df1[1:]
删除重复行
print(df1.iloc[:,0:5].drop_duplicates())#去除相同的行
行查询
df.columns=['os','app_version','event','item_type','e_item_id','i_item_id','i_item_type'] df_group=df.groupby(['os','app_version','event']) successful_df_list=[] unsuccessful_df_list=[] for title,item in df_group: successful_df=item.loc[(item['i_item_id'] !=numpy.NaN) & (item['item_type'] ==item['i_item_type'])] unsuccessful_df=item.loc[~((item['i_item_id'] !=numpy.NaN) & (item['item_type'] ==item['i_item_type']))]
合并操作
merge(left_on='gender', right_on='gender1')
左边和右边的df中相同的部分去掉,不同的部分显示出来;(求2者差集)
xls = pd.ExcelFile('pandas.xlsx') df1 = xls.parse( header=None) df1.columns=['name','age','gender'] xls = pd.ExcelFile('pandas1.xlsx') df2 = xls.parse( header=None) df2.columns=['name1','age1','gender1'] df3=df1.merge(df2, left_on='gender', right_on='gender1')#左边表里的和右表里面的列是一样的我就合并 print(df3)
性能提升
1.使用 joblib 模块对 Pandas加速
joblib不仅可以保存和加载模型,竟然也可以开进程。
from math import sqrt from joblib import Parallel, delayed def test(): start = time.time() result1 = Parallel(n_jobs=1)(delayed(sqrt)(i**2) for i in range(10000)) end = time.time() print(end-start) result2 = Parallel(n_jobs=8)(delayed(sqrt)(i**2) for i in range(10000)) end2 = time.time() print(end2-end)
如果需要对一个很大的数据集进行操作,而基于一列数据生成新的一列数据可能都需要耗费很长时间。
于是可以使用 joblib 进行并行处理,但是要恰到好处的使用进程和线程。
如果apply的函数逻辑简单,并行处理比单次执行效率更慢。因为CPU调度进程也需要花费时间。
import pandas as pd from joblib import Parallel, delayed class DataframeUtil(object): @staticmethod def parallel_apply(df, n_jobs, job): df_grouped = df.groupby(df.index) results = Parallel(n_jobs=n_jobs)(delayed(job)(group) for name, group in df_grouped) united_df = pd.concat(results) return united_df @staticmethod def average_calculation(df, job, job_args, cpu=4): total = df.shape[0] quot, rem = divmod(total, cpu) index_list = [i for i in range(0, total, quot)] index_list.append(total) step_list = [[index_list[i], index_list[i + 1]] for i in range(0, len(index_list)) if i < len(index_list) - 1] results = Parallel(n_jobs=cpu)(delayed(job)(df[step[0]:step[1]], job_args) for step in step_list) return results @staticmethod def test(df, args): print(df) print(args) if __name__ == '__main__': df = pd.DataFrame({"num": range(10000000)}) # 创建dataframe,包含1千万行 DataframeUtil.average_calculation(df, DataframeUtil.test, {"name": 'zhanggen'})
2.遍历Dataframe的3种方式对比
在实际开发过程中我们能使用apply解决的遍历问题,都不要使用for循环。
import pandas as pd from sqlalchemy import create_engine corpus_database_uri = "sqlite:///D:/mingde/projects/budget-api/profile/db/filling.db3" sql = """ SELECT feature as fpName, category as fpType, category_audit as auditType FROM oss_detail_function d LEFT JOIN oss_project_item i ON d.c_code=i.code """ corpus_engine = create_engine(corpus_database_uri) df = pd.read_sql(sql, corpus_engine) # 方式1:df.iterrows() # 时间花费在性能检查 for index, row in df.iterrows(): print(row["fpName"], "-----------", row["auditType"]) # 方式2:itertuples() # 时间花费在构建namedtuple对象 for row in df.itertuples(): print(row.fpName, "-----------", row.auditType) # 方式3:for + zip的方式 # 原生tuple的性能 for a, b in zip(df["fpName"], df["auditType"]): print(a, "-----------", b)