【Pandas】实操手册

文章目录

一、读取数据

1.导包
import pandas as pd
2.从csv导入数据
pd.read_csv(filepath_or_buffer, sep=',', delimiter=None, header='infer', 
			names=None, index_col=None, usecols=None, squeeze=False, 
			prefix=None, mangle_dupe_cols=True, dtype=None, engine=None, 
			converters=None, true_values=None, false_values=None, 
			skipinitialspace=False, skiprows=None, nrows=None, 
			na_values=None, keep_default_na=True, na_filter=True, 
			verbose=False, skip_blank_lines=True, parse_dates=False, 
			infer_datetime_format=False, keep_date_col=False, 
			date_parser=None, dayfirst=False, iterator=False, chunksize=None,
			compression='infer', thousands=None, decimal=b'.', 
			lineterminator=None, quotechar='"', quoting=0, escapechar=None, 
			comment=None, encoding=None, dialect=None, tupleize_cols=None,
			error_bad_lines=True, warn_bad_lines=True, skipfooter=0,
			doublequote=True, delim_whitespace=False, low_memory=True,
			memory_map=False, float_precision=None)
df=pd.read_csv('file.csv',encoding='utf')

如果文件名带中文,上述方法可能报初始化错误,使用以下方法:
f=open('文件.csv')wirh open('文件.csv') as f:
df = pd.read_csv(f)

with open('文件.csv',encoding='utf-8') as f:
    df=pd.read_csv(f)

该方法其实有很多参数可以实现很多功能,比如在读取文件的时候设定参数na_value='值‘,就可以将该表中为该值的数据当做成np.nan,当然这些功能也可以在read之后实现。参考:pandas系列 read_csv 与 to_csv 方法各参数详解(全,中文版)

  • tip:批量读表
#first订单1-3.csv和其他以”订单“开头的表结构相同
df_orderpd.read_csv(r'first订单1-3.csv',dtype={'员工邀请码': 'str'})
#https://laowangblog.com/python-pandas-csv-dtypewarning-mixed-types.html
pat=re.compile('订单.*?')
for file_name in os.listdir(os.getcwd()):
    if re.match(pat,file_name):
        df_order=pd.concat([df_order,pd.read_csv(file_name,dtype={'员工邀请码': 'str'})])
3.从excel导入文件

pd.read_excel(file.xlsx')

df_order=pd.read_excel('file名.xlsx')

该方法接受文件名带中文

pd.read_excel(io, sheet_name=0, header=0, names=None, index_col=None, 
			  usecols=None, squeeze=False, dtype=None, engine=None, 
			  converters=None, true_values=None, false_values=None, 
			  skiprows=None, nrows=None, na_values=None, parse_dates=False, 
			  date_parser=None, thousands=None, comment=None, skipfooter=0, 
			  convert_float=True, **kwds)

该方法有很多参数可以应对不同的情况,例如指定工作表等。

4.从数据库读取数据

连接数据库参考:
[Python]第十三章 数据库支持
【Python小笔记】Python连接Oracle数据库——cx_Oracle

pandas.read_sql(sql, con, index_col=None, coerce_float=True, params=None, 
				parse_dates=None, columns=None, chunksize=None)

sqllite3

import pandas as pd
import sqllite3
with sqllite3.connect('country_stat.sqlite') as cons:
    dfs = pd.read_sql('SELECT * FROM regional_gross_product WHERE year = 2015 ORDER BY gross_product DESC LIMIT 3', con = cons)
dfs.head()

mysql
没有自带__enter__() 和 __exit__() 方法,不能直接使用with上下文管理器,推荐阅读使用with语句优化pymysql的操作

import pymysql
conm=pymysql.connect('localhost','root','123456','mytest')
dfm=pandas.read_sql('SELECT * FROM EMPLOYEES',con=conm)
conm.close()
dfm.head()

oracle

import cx_Oracle
name='scott'
pwd='tiger'
add='localhost/orcl.16.2.133'
with cx_Oracle.connect(name,pwd,add) as cono:
    dfo=pandas.read_sql('SELECT * FROM EMP WHERE DEPTNO=20',con=cono)
dfo.head()
5.读取网页表格
pd.read_html(io, match='.+', flavor=None, header=None, index_col=None, 
			skiprows=None, attrs=None, parse_dates=False, tupleize_cols=None,
			thousands=',', encoding=None, decimal='.', converters=None, 
			na_values=None, keep_default_na=True, displayed_only=True)

二、属性更改

1.针对某(些)个字段(列)|索引更名

df.rename(mapper=None, index=None, columns=None, axis=None, copy=True, inplace=False, level=None)

df_order.rename(columns={'下单日期':'日期','下单小时':'小时'},inplace=True)
或
df_order.rename({'下单日期':'日期','下单小时':'小时'},axis='columns',inplace=True)
#inplace=True对原dataframe进行修改

该方法可以更改列名或行名或更改标签值类型
如果需要改的列名特别多,可以直接给df的columns属性赋值,修改全部列名

df.columns=['年','周','频道','性别','销售数量','销售金额']

三、结构变动

1.插入行
a.在指定位置插入:
STEP1:分割
df=pd.DataFrame([list('abc'),list('anf'),list('abc'),list('fgc'),list('rbc')])
df_p1=df[:2]
df_p2=df[2:]
STEP2:添加——底部追加内容

df.append(other, ignore_index=False, verify_integrity=False, sort=None)

ser_m=pd.Series([44,55,66])
df_p1=df_p1.append(ser_m,ignore_index=True)
STEP3:合并——多个dataframe堆叠

pd.concat(objs, axis=0, join='outer', join_axes=None, ignore_index=False, keys=None, levels=None, names=None, verify_integrity=False, sort=None, copy=True)
该方法不仅可以上下堆叠,还可以左右堆叠

df=df_p1.append(df_p2,ignore_index=True)
或
df=pd.concat([df_p1,df_p2],ignore_index=True]
b.默认位置(尾部):

append方法可以快速在底部追加

df=df.append(ser_m,ignore_index=True)
df=df.append(df_new,ignore_index=True)

concat不截断,就是默认在底部合并

df=pd.concat([df,df_new],ignore_index=True]

如果知道要添加的行的索引,直接赋值(注意如果索引号错误则会覆盖原df数据)

df.iloc[5]=ser_m

参考:【Python数据科学手册】Pandas——七、合并数据集:Concat和Append操作

2.插入列
a.在指定位置插入:

df.insert(loc, column, value, allow_duplicates=False)

df.insert(7,'brand_supplier',brand_supplier)

插入的列除了源dataframe结构的序列,也可以是常量,或者空值np.nan
但是这种方法只能一次插入单列

b.默认插入(尾部):

如果不需要指定位置插入,直接单列赋值,如df['new']=11,会在最后列添加’new’列
指定列方向,使用concat方法也可以合并列,如果需要指定位置,可以插入后调整列序

pd.concat([df,df_new],axis=1)
3.一般方法删除指定行或列(按照索引或列名)
a.删除行

(1). 先获取索引,再使用drop方法删除

STEP1:获取记录index

获取索引的方法见下文<单列条件筛选记录>
df[条件序列].index
ser[条件序列].index

del_index=df[df['subcategory'].isin(list_type)].index
ser_nor[ser_nor>1].index
STEP2:删除索引所在行

df.drop(labels=None, axis=0, index=None, columns=None, level=None, inplace=False, errors='raise')
其中:axis=0代表行,axis=1代表列

df=df.drop(del_index,axis=0,inplace=True)

(2). 如果条件不复杂,条件取反后赋值给原df

df=df.query('ID!=12123')
df
b.删除列

del df[列名]
df=df.drop(列名,1)
df.drop(列名,axis=1,inpalce=True)

df_house.drop('Brick',axis=1)
df_house.drop(['No','West','Brick','Neighborhood'],axis=1)

如果需要删除的列太多而要保留的列很少,或者只需要从原df中选取指定的某(几)列,可以列表选取后直接赋值
df=df[['列名1','列名2',...]]
当然列表中也可以只有一个元素,表示只有一列的dataframe,需要与df['列名']这个Series区别
df=df[['列名']]

4.调整列顺序

如果仅调整某列的位置,可以取出-删除-插入

df_id = df.id
df = df.drop('id',axis=1)
df.insert(0,'id',df_id)

如果要调整的列比较繁杂,可以直接定义好列序,赋值给原df即可

df=df[['姓名','年龄','工号']]

或者使用reindex方法(reindex方法还可以插入空列)

df_sp=df_sp.reindex(columns=list('卫室厅厨'))
5.去除重复记录

去除重复记录,可以用删除的一般方法,先用duplicated()找到重复数据的索引,再drop删除,也可以直接使用:
df.drop_duplicates(subset=None, keep='first', inplace=False)
df.drop_duplicates(subset=['姓名','性别'], keep='first', inplace=False)

6.删除空行|空列

df.dropna(axis=0, how='any', thresh=None, subset=None, inplace=False)

axis:0删除行,1删除列
how:'any’表示一旦有缺失值就删除,'all’表示全部是缺失值才删除
thresh:设置缺失值个数的删除边界,一旦超过某值,就删除
subset:要删除的行或列的标签列表
inplace:是否更新到原df

#删除’作者ID‘列有空值的所在行
df_cont.dropna(axis=0,subset=['作者ID'],inplace=True)
7.连接merge

merge操作类似于excel中vlookup的存在,在sql中效通join,一般会伴随这字段的新增或更改。
df.merge(right, how='inner', on=None, left_on=None, right_on=None, left_index=False, right_index=False, sort=False, suffixes=('_x', '_y'), copy=True, indicator=False, validate=None)

df=df1.merge(right=df2,how='left',on=['店铺名称','日期','小时'])
df_A=df_A1.merge(right=df_A2,how='left',left_on=['公司名','业务主体'],right_on=['公司名','业务线'])
8.透视

df.pivot_table(values=None, index=None, columns=None, aggfunc='mean', fill_value=None, margins=False, dropna=True, margins_name='All')

df_pv_pivot=df_pv.pivot_table(index=['资讯ID','日期'],values='PV',aggfunc='sum')

参考:【Python数据科学手册】Pandas——十、数据透视表 df. pivot_table

9.排序

df.sort_index()按索引排序
df.sort_value()按某列值排序
df.rank()排名
注意:通过axis参数可以指定行排序

df.sort_values(by='PV',ascending=False)

如果需要取topN,或者倒序取topN,可以先排序后切片

df.sort_values(by='销售',ascending=False).iloc[:5]

也可以使用df.nlargest()df.nsmallest()直接取

df.nlargest(5,'销售')

对于分组取topN,可以使用以下函数,内涵也是先排序后切片

#分组df取topN
def func(x,N,asc):
    return x.sort_values('population',ascending=asc)[:N]
df.groupby('continent').apply(func,N=3,asc=False)
10.get_dummies() 方法-按照内容分列,以0-1作为布尔结果填充

get_dummies()
作为普通方法,将整个值内容提取作为新列

pd.get_dummies(df['朝向'])

作为str方法,按照特定符号提取字符作为新列

df['info'].str.get_dummies(',')

四、筛选数据

1.按照行列名来筛选行或者列

单列df[列名],多列可以使用df.filter(),改方法也可筛选行

#取列名取列
df.filter(regex='.*销.*')
df.filter(items=['销售'])
df.filter(like='销')
#取索引名取行
df.set_index('客户').filter(like='华鼎',axis='index')
#先用set_index方法给每一行设置一个索引名作为行名,再去筛选这个行名
2.按照列值条件来筛选记录

选取数据的方法有很多种,本节简单使用df[条件]选取符合条件的行记录,列全部保留。
实际操作中使用索引器loc\iloc可以灵活选取所需数据,包括列的选择,例如

df[df['ID']==12121].iloc[:,2:5]#df[条件].iloc[:,列号:列号]

等价于掩码操作

df.loc[df['ID']==12121,'标题':'编辑']#df.loc[条件,列名:列名]

也可以使用df.query()方法取,适用于运算条件,例如:

df.query('ID==12121').loc[:,'标题':'编辑']#df.query(字符串表达式).loc[:,列名:列名]

参考 【Python数据科学手册】Pandas——三、数据取值与选择
参考 【Python数据科学手册】Pandas——十三、高性能的Pandas:eval()和query()

(1).单列条件筛选记录
1).筛选某列包含某字符串的记录

df[df[列名].str.contains(正则表达式)]

df[df['brand_supplier'].str.contains('.*华鼎.*')]
2).从某列筛选等于|不等于某值的记录

不等于:!=
df[df[列名]==值]
df[df[列名]!=值]

df[df['shop_name']=='XXXTRENTA']
df[df['shop_name']!='XXXTRENTA']
3).从某列筛选等于|不等于某些值的记录

非:~
df[df[列名].isin(列表)]
df[~df[列名].isin(列表)]

list_type=['运动','美妆','男装','女装']
df[df['subcategory'].isin(list_type)]
df[~df['subcategory'].isin(list_type)]
4).找到重复的记录 | 非重复的记录-提取汇总项

(同样的记录第一次出现是非重复,之后又出现的都算重复)
df[df.duplicated(subset=None, keep='first')]
df[~df.duplicated(subset=None, keep='first')]
subset是列名或列名集合,用它来定义重复,缺失默认全字段重合的数据才是重复。

#重复的记录
df_passenger[df_passenger.duplicated('店铺名称')]
#非重复的记录
df_passenger[~df_passenger.duplicated('店铺名称')]

EXTEND:
如果要提取汇总项,除了上述方法找到非重复记录后取该列,

df_passenger[~df_passenger.duplicated('店铺名称')]['店铺名称']#pandas.core.series.Series

也可以用分组groupby汇总项索引转列后提取使用

df_passenger.groupby(by='店铺名称').count().reset_index()['店铺名称']#pandas.core.series.Series

或者df[列名].unique()——最快捷

df_passenger['店铺名称'].unique()#numpy.ndarray, len()得到非重复个数

甚至可以对汇总项快速计数df[列名].value_counts()

df_passenger['店铺名称'].value_counts()#pandas.core.series.Series
5).找到空值记录|非空值记录

判断某行或者某列是否有空值
df.any()存在
df.all()全部

#查看全列
df.isnull()#返回一个dataframe,和原dataframe的结果一一对应
df.isnull().any()#得到一个序列对应每列的结果
df.isnull().values.any()#得到一个布尔值,反应整体的结果
df.isnull().sum()#加总布尔值True等价于1,合计每列有多少个缺失值
df.isnull().sum().sum()#将所有列的缺失值都加总
#或者仅查看某列
df['客户'].isnull()#得到一个序列对应改列每一个值的结果
df['客户'].isnull().any()#得到该列的整体结果
df['客户'].isnull().sum()#合计该列的缺失值数

空值记录:df[pd.isna(df[列名])]等价于df[pd.isnull(df[列名])]

df[pd.isna(df['name'])]

非空值记录:df[pd.notna(df[列名])]等价于df[pd.notnull(df[列名])]

(2).多个列组合筛选

与:df[(条件列筛选序列1)&(条件列筛选序列2)]
或:df[(条件列筛选序列1)|(条件列筛选序列2)]
异或:df[(条件列筛选序列1)^(条件列筛选序列2)]

df[(df['brand_supplier'].str.contains('.*华鼎.*'))&(df['shop_name']!='XXXTRENTA')]

五、列的值处理

1.填充空值
a.直接填充

df.fillna(value=None, method=None, axis=None, inplace=False, limit=None, downcast=None, **kwargs)

单列填充空值df[列名].fillna(值)
全表填充空值df.fillna(值)

这里的value可以是scalar, dict, Series, or DataFrame,不能是list

df['subcategory'].fillna('其它')
df['age'].fillna(df['age'].mean())#填充平均值
df['age'].fillna(df.groupby('gender')['age'].transform('mean'))#填充分组平均值

transform参考:【Python数据科学手册】Pandas——九、累计与分组groupby

method:‘pad’、'ffill’向后填充(用上一位的值填空值),‘bfill’、'backfill’向前填充
limit:要填多少个值

df.fillna(method="bfill", limit = 2)
b.规律填充(内插法)
df2 = pd.DataFrame([[1, 870],\
                    [2, 900],\
                    [np.nan, np.nan],\
                    [4, 950],\
                    [5,1080],\
                    [6,1200]])
df2.columns = ['time', 'val']
df2.interpolate()

详细参考scipy库官方文档interpolate部分

2.单列赋值

df[列名]=值
这里的值可以是函数应用公式(见第七节),也可以是常量

df['channel']='淘宝'

如果原先没有该列,则会默认在dataframe的最后添加新列。

2.替换单个值和批量替换值

(1)单值替换
方法1:定位要替换的值后直接赋替换后的值:df.loc[df[列名]==被替换的值,列名]=值

df.loc[df['subcategory']=='关店','subcategory']='其它'

方法2:replace方法df.replace(to_replace=None, value=None, inplace=False, limit=None, regex=False, method='pad')

df['subcategory']=df['subcategory'].replace('关店','其它')

(2)批量替换

  • 一对一
#方法一:使用列表,保证顺序对应
xlist=['A','B','C']
ylist=['a','b','c']
#for o,l in zip(xlist,ylist):
#	df[列名]=df[列名].replace(o,l)
df[列名].replace(xlist,ylist,inplace=True)
#方法二:使用字典
mapdict={'A':'a','B':'b','C':'c'}
#for m in maplist:
#	df[列名]=df[列名].replace(m,mapdict[m])
df[列名].replace(mapdict)
  • 多对一
#条件查找,模糊替换
df.loc[pd.to_numeric(df_pp.loc[:,'年季'].str[0:2])<=17,'年季']='17年以前'
#确值替换
df['姓名'].replace(['张倩','张茜'],'张芊')
3.类型转换:日期-数值-字符串转换

主要方法有:

  • pandas内置函数、
  • 自定义函数、
  • numpy中的强制类型交换方法astype(dtype, order='K', casting='unsafe', subok=True, copy=True)
    | b boolean
    | i signed integer
    | u unsigned integer
    | f floating-point
    | c complex floating-point
    | m timedelta
    | M datetime
    | O object
    | S (byte-)string
    | U Unicode
    | V void
    参考:https://www.jb51.net/article/175212.htm
1)字符串类型的数据,转换成日期

对于形似日期的字符串数据,可以转换成真正的日期

  • 对于单个数据:
    from dateutil import parser
    parser.parse(string)
from dateutil import parser
date=parser.parse('2020-6-1')
date
>>>datetime.datetime(2020, 6, 1, 0, 0)
  • 对于pandas序列:
    pd.to_datetime(df[列名])
pd.to_datetime(df_order['日期'],format="%Y-%m-%d")#这里format是传入的格式

或者
df[列名].astype('M')datetime64[ns]

2)日期转成指定样式的字符串
  • 对于单个数据:
    date.strftime(样式)
date.strftime("%Y%m%d")
>>>'20200601'
#获取当天日期
import time
time.strftime('%Y%m%d')
#或者
import datetime
datetime.date.today().strftime('%Y%m%d')
#动态相对日期(两天前)
(datetime.date.today()-datetime.timedelta(2)).strftime('%Y-%m-%d')

datetime库参考:Python中datetime库的用法
时间相关资料:
[Python]第十章 开箱即用
【Python数据科学手册】Pandas——十二、处理时间序列

  • 对于pandas序列,没有直接的转换方法,需要使用函数转换
    df[列名].apply(lambda x:x.strftime(指定格式))
pd.to_datetime(df_order['日期']).apply(lambda x:x.strftime("%Y%m%d"))
或者
def dtos(x):
    return x.strftime("%Y%m%d")
pd.to_datetime(df_order['日期']).apply(dtos)
3)字符串转数值
  • 对于单个数据,直接int(string)即可
  • 对于pandas序列
    pd.to_numeric(df[列名])
df_order['日期']=pd.to_numeric(pd.to_datetime(df_order['日期']).apply(lambda x:x.strftime("%Y%m%d")))

或者
df[列名].astype('int')int32

4)数值转字符串
  • str(number)
df_order['年龄'].apply(lambda x:str(x))

或者
df[列名].astype('str')object

5)从日期提取数值
  • 对于单个数据
import datetime
d=datetime.datetime.now()
d
>>>datetime.datetime(2020, 8, 26, 10, 19, 25, 910701)
#年、月、日、时、分、秒、微秒
d.year,d.month,d.day,d.hour,d.minute,d.second,d.microsecond
(2020, 8, 26, 10, 19, 25, 910701)
  • 对于pandas序列
#year年, month月, day日,date日期,hour小时
df['日期'].dt.date
  • 取相差天数
(pd.to_datetime('2020-12-25',format='%Y-%m-%d')-df_rfm['ORDERDATE']).dt.days

六、字符串内容的处理

这里是对字符串值的字符内容的处理,与字段内容处理不同,例如替换值,excel里面是ctrl+h,python里参考本文,可以用df[A].replace()方法;
而本节是针对字符串内容的处理,在excel中替换字符用的是substitute函数,python里面需要df[A].str.replace()
大部分在python基础中学习的对字符串的方法,这里都能实现

1.由现有的两列拼接成一个新的字段

df[A].str.cat(df[B])

brand_supplier=df['brand_name'].str.cat(df['supplier_name'])

如果两个字段是形同类型的字符串,也可以直接用加号

brand_supplier=df['brand_name']+df['supplier_name']

但是如果有字段不是字符类型,需要先将该字段转成字符类型

df['总价'].astype('str').str.cat(df['价格单位'])
2.查找子串在字符值中的位置

df[A].str.find(childstring)

df['店铺名称'].str.find('餐厅')
3.返回指定位置的字符

返回单个字符df[A].str.get(index)

df['店铺名称'].str.get(0)

返回片段用切片

df['店铺名称'].str[:3]
4.判断字符值中是否包含子串

df[A].str.contains(childstring)

df['店铺名称'].str.contains('餐厅')
5.计算子串在字符值中的出现次数

df[A].str.count(childstring)

df['店铺名称'].str.count('餐厅')
6.替换字符值中的子串

df[A].str.replace(原字符,后字符)

df['姓名'].str.replace('三','二')#将‘三替’换成‘二’

字符串中的replace一般只可以一个字符串对应一个字符串替换,不能是列表或字典。

7.去除字符串前后空格或指定字符
#建筑年代:1996\r\n
df['age'].str.strip().str.strip('建筑年代:')
>>>1996

df['age'].map(lambda e:e.strip().strip('建筑年代:') )
8.根据字符分割整字符串,再截取子串

df[A].str.split(childstring)

df['姓名'].str.split('三').str[-1]
df['室']=df['户型'].str.split('室').str[0]
9.字符大小写
df_passenger['店铺名称'].str.lower()
df_passenger['店铺名称'].str.upper()
10.使用函数-实现字符处理

除了str内置方法,也可以根据需求定制处理函数,通用的函数方法具体见下节。
举例:

  1. 根据字符分割整字符串,截取子串
    split()分割字符串,分割后生成子字符串列表,根据列表索引选择要截取的部分
df['姓名'].apply(lambda x:x.split('三')[-1])

效果同上一小节
2) 字符大小写
小写lower()大写upper()

df_passenger['店铺名称'].apply(lambda x:x.lower())
df_passenger['店铺名称'].apply(lambda x:x.upper())

11.使用正则表达式
#st='2室2厅1厨1卫'
df[list('室厅厨卫')] = df['户型'].str.extract('(\d+)室(\d+)厅(\d+)厨(\d+)卫')

'''
df_sp=df['户型'].str.extract('(\d+)室(\d+)厅(\d+)厨(\d+)卫')
#df['室'],df['厅'],df['厨'],df['卫']=[df_sp.iloc[:,i] for i in range(4)]
df_sp.columns=list('室厅厨卫')
df=pd.concat([df,df_sp],axis=1)
'''

七、构建函数公式计算字段

上一节字符内容的处理也是计算字段,包括转换类型部分也涉及到。本节介绍更通用的函数方法处理字段值。
要传入的计算对象.apply(func, axis=0, broadcast=None, raw=False, reduce=None, result_type=None, args=(), **kwds)
要传入的对象可以是df,也可以是series、groupbySeries
func可以是匿名函数或者自定义函数

1.函数写法

1.apply()是一种让函数作用于DataFrame中行或列的操作。
2.applymap()是一种让函数作用于DataFrame每一个元素的操作。
3.map()是一种让函数作用于Series每一个元素的操作。

(1).匿名函数

df['ID'].apply(lambda x: 'Boy' if x>30000 else 'Girl')
#是否包含任意关键词
keycategorylist=df_hx['核心品类'].tolist()
df_order['核心品类商品']=df_order['商品品类'].apply(lambda x:'核心' if any(kt in str(x) for kt in keycategorylist) else '非核心')
##是否包含某个子串
df_plat['本月新增商户']=df_plat_order['收入时间'].apply(lambda x:'新增' if year_month in str(x) else '非新增')

(2).自定义函数

  • 传入series
def compare(x):
    if x>30000:
        y='Boy'
    else:
        y='Girl'
    return y
df['ID'].apply(compare)#传入series,不需要指定axis
#df['ID'].map(compare)#map方法可以将函数套用到Series上的每个元素
  • 传入df,指定方向
def dfcompare(x):
    if x['ID']>30000:
        y='Boy'
    else:
        y='Girl'
    return y
df.apply(dfcompare,axis=1)#传入df,axis=1指定逐行

CASE:结合re模块,利用正则表达式拆分字段成新列,并转换数据类型
split()函数无法依据多个符号拆分字符串,因此可以用到正则表达式的编组
(如上节所示,其实pandas字符串自带re模块函数方法,但是extract()方法是编组后直接返回,中间不能插入转换类型这一操作,只能返回后一列列转换)
分组-返回-转换

cols=list('室厅厨卫')
df[cols] = df['layout'].str.extract('(\d+)室(\d+)厅(\d+)厨(\d+)卫')
for i in range(4):
	df[cols[i]]=pd.to_numeric(df[cols[i]])

以下方法自己写的函数,思路是先分组-转换-最后返回新列

#st='2室2厅1厨1卫'
from re import *
pat=compile('(\d+)室(\d+)厅(\d+)厨(\d+)卫')
def pa(x):
    m=match(pat,x)    
    nums=[m.group(1),m.group(2),m.group(3),m.group(4)]
    return nums
for i in range(4):
    df[colli[i]]=pd.to_numeric(df['layout'].map(pa).map(lambda x:x[i]))

pandas的str本身就有re模块相对应的方法,相对而言,正则匹配相对re模块要宽松,

s='中层(共7层)\r\n'
#pandas
pat=compile('\s*(\d+)室(\d+)厅\s*')
#re
df['floor_info'].str.extract('共(\d+)层')
2.公式写法

参考:
【Python数据科学手册】Pandas——十三、高性能的Pandas:eval()和query()
a.常规:

#(入库金额-退供应商金额)*90天售罄/100
df_sq['收入']=(df_sq['销售']-df_sq['退货'] )* df_sq['90天均价']/100

b.numpy的通用函数:

df['收入']=df['销售'].sub(df['退货']).mul(df['90天均价'])/100

c.df.assign:

df=df.assign(收入=lambda df:(df['销售']-df['退货'])*df['90天均价']/100)

d.df.eval():

nisq=df_sq['90天均价']
df_sq['收入']=df_sq.eval('(销售-退货)*@nisq/100')
#列名不规范,可以先定义变量
#df_sq['收入']=df_sq.eval('(销售-退货)*90天均价/100')
#报错,不能直接使用带数字的不规范列名

e.pd.eval():

#df_sq['收入']=pd.eval("(df_sq.销售-df_sq.退货 )*df_sq.90天均价/100")
#报错,【df_sq.90天均价】写法错误

f.其它
原则上可以通过以上方法构建大部分公式获得新列,但是pandas提供了许多原生的方法,比如计算环比的方法pct_change()就是用本项减去上一项的差除以上一项,含义就是变动百分比,比自定义函数方便,这些方法工作中注意积累。

df['ret'] = df['Close'].pct_change(1)

八、分组

1.分组

df.groupby(by=None, axis=0, level=None, as_index=True, sort=True, group_keys=True, squeeze=False, observed=False, **kwargs)

df.groupby(by=['brand_supplier','shop_name'])

注意 :对于多重分组,如果但凡一个分组依据列有空值,那么分组后所有涉及该空值所在组都会被删除,因此在多重分组之前,最后在这几列空值填充一下

df_cont[['作者ID','作者身份','文章类型']]=df_cont[['作者ID','作者身份','文章类型']].fillna('缺失')
df_contf=df_cont.groupby(by=['文章ID','作者ID','作者身份','文章类型']).sum()[['曝光量','浏览量']].reset_index()
2.分组聚合计算

DataFrameGroupBy.函数()

df.groupby(by=['brand_supplier','shop_name']).sum()
3.取指定列来分组聚合计算/分组聚合计算后取指定列

DataFrameGroupBy.函数()[列名]/
DataFrameGroupBy[列名].函数()

ser_nor=df_nor.groupby('brand_supplier').count()['shop_name']
或者
ser_nor=df_nor.groupby('brand_supplier')['shop_name'].count()
#多指标
ser_nor=df_nor.groupby('brand_supplier')[['shop_name','fuid']].count()
4.分组聚合指定列排序后取topN
orders.groupby('User')['Total_price'].sum().sort_values(ascending=False)[0:10]

九、索引—列的转换,索引-维度转换

参考:【Python数据科学手册】Pandas——六、层级索引

1.索引转列

groupby后,by字段会变成index,如果需要转成正常列,操作如下
索引转列reset_index()

df_nor=df.groupby(by=['brand_supplier','shop_name']).sum().reset_index()
#'brand_supplier','shop_name'原本是index,转成正常列侯,也由Series变成了dataframe
2.列转索引

列转索引set_index()

3.索引-维度

unstack

df_plat_order.groupby(by=['分部','开单挂零']).agg({'公司名':'count'}).unstack()

在这里插入图片描述在这里插入图片描述

4.维度-索引

stack

参考:【Python数据科学手册】Pandas——六、层级索引
也可以考虑使用数据透视表功能
参考:【Python数据科学手册】Pandas——十、数据透视表 df. pivot_table

df_plat_order.groupby(by=['分部','开单挂零']).agg({'公司名':'count','实际支付金额':sum}).unstack()
df_plat_order.pivot_table(index='分部',columns='开单挂零',aggfunc={'公司名':'count','实际支付金额':sum})

十、文件存储

1.保存到excel

pd.to_excel()
ser.to_excel()

 ser[ser!=0].to_excel('G:\\work files\\file.xlsx',encoding='utf')
 df[df['amount']!=0].to_excel('G:\\work files\\file.xlsx',encoding='utf')

如果多个df或series保存到同一个excel的不同sheet,需要用到pd.ExcelWriter()作为存储目标

with pd.ExcelWriter('G:\\work files\\work_from_20\\'+filename+'.xlsx') as exf:
    df_a.to_excel(exf,sheet_name='编辑',index=False)
    df_b.to_excel(exf,sheet_name='优质用户',index=False)
2.同名文件避免被替换:同一文件夹文件重名时增序命名
impoert os
filepath='文件存放路径'
newfile_name='带后缀的完整文件名'
i=0
while newfile_name in os.listdir(filepath):#如果当前路径已经存在文件
    i=i+1#指定增序
    newfile_name='文件名'+'('+str(i)+')'+'.xlsx'#要保存的文件重命名:在文件名后加序号
#循环至没有同名文件
newfile=filepath+'\\'+ newfile_name#要保存的完整路径文件名

df.to_excel(newfile,index=None)
3.保存到数据库

df.to_sql(name, con, schema=None, if_exists='fail', index=True, index_label=None, chunksize=None, dtype=None)
官方文档:pandas.DataFrame.to_sql
关于con参数,文档中写到如下,这意味着,con只支持sqlalchemy.engine和sqlite3.Connection。
使用SQLAlchemy可以使用该库支持的任何数据库。 为sqlite3.Connection对象提供了旧版支持。

con :sqlalchemy.engine.(Engine or Connection) or sqlite3.Connection

如果需要指定数据类型,会用到dtype参数,参考Python sqlalchemy.types模块,常用函数和类
sqllite3

import sqlite3
with sqlite3.connect('test.sqlite') as conn:
    df.to_sql(name = 'employee', con = conn, if_exists='replace', index = None)

mysql

from sqlalchemy import create_engine
connect_info = 'mysql+pymysql://root:123456@localhost:3306/mytest'
engine=create_engine(connect_info)
df.to_sql('product', con = engine, if_exists='replace', index=None)

oracle
参考: sqlalchemy.exc.DatabaseError: (cx_Oracle.DatabaseError) ORA-12505: TNS: 监听程序当前无法识别连接描述符中所给出的 SID

import cx_Oracle
from sqlalchemy import create_engine
dnsStr=cx_Oracle.makedsn('localhost', '1521', service_name='orcl.16.2.133')
connect_info = 'oracle://scott:tiger@%s' %(dnsStr)
engine=create_engine(connect_info)
df.to_sql('product', con = engine, if_exists='replace', index=None)

CASE

插入拼接列
并剔除有多个shop_name的brand_supplier重复数据

import sys
def main(inda):
    import pandas as pd
    
    df=pd.read_csv('G:\\temp files\\file.csv',encoding='utf')

    #插入新字段
    brand_supplier=df['brand_name'].str.cat(df['supplier_name'])#拼接两个字段成一个新字段
    df.insert(7,'brand_supplier',brand_supplier)#插入新字段到dataframe

    #删除“华鼎”系列重复项
    del_col=df[(df.brand_supplier.str.contains('.*华鼎.*'))&(df.shop_name!='XXXTRENTA')].index#获取要删除数据的索引号(品牌供应商名包含“华鼎”且店铺不为"XXXTRENTA")
    df.drop(del_col,axis=0,inplace=True)#删除获得的索引号所在行记录

    #获得有重复店铺数据的品牌供应商
    df_nor=df.groupby(by=['brand_supplier','shop_name']).sum().reset_index()#多维度分组,随意指定一个计算行数sum(),方便将index'brand_supplier','shop_name'转为DataFrame普通列
          
    ser_nor=df_nor.groupby('brand_supplier').count().shop_name#取到分组序列,索引号是分组依据字段'brand_supplier'
    dup_list=list(ser_nor[ser_nor>1].index)#筛选计数大于1即店铺有多个的'brand_supplier'的列表


    #删除多余店铺
    for bs in dup_list:
        shnm_list=list(df[df.brand_supplier==bs].groupby('shop_name').count().reset_index().shop_name)#多店铺的brand_supplier下的各shop_name列表
        brnm=df[df.brand_supplier==bs].brand_name.iloc[0]#brand_supplier对应的brand_name(唯一),所以只要取一个就行

        #逻辑:如果多个店铺名中至少有跟品牌同名的,那就将该brand_supplier下店铺名不等于品牌的数据删掉(保留店铺名与品牌有同名关系的店铺)
        if brnm.lower() in [shlw.lower() for shlw in shnm_list]:#忽略大小写需要同时转大写或小写才能比较
            for sn in shnm_list:#将shop_name和brand_name对比
                if sn.lower()!=brnm.lower():#如果shop_name和brand_name不能完全相等
                    del_col_sh=df[(df.brand_supplier==bs)&(df.shop_name==sn)].index#那就找到该brand_supplier下的该shop_name
                    df.drop(del_col_sh,axis=0,inplace=True)#删除
        #逻辑:如果店铺名没有一个与品牌名同名,那就只保留subcategory不为空的店铺
        else:
            list_type=['运动','美妆','男装','女装'']

            del_col_su=df[(df.brand_supplier==bs)&(~df['subcategory'].isin(list_type))].index#找到该brand_supplier下subcategory不为列表中项的数据,就是subcategory空
            df.drop(del_col_su,axis=0,inplace=True)
            
                
    #导出所需透视图
    df.subcategory=df.subcategory.fillna('其它')#空值和关店改成其它
    df.loc[df.subcategory=='关店','subcategory']='其它'

    ser=df.groupby(['shop_name','subcategory']).sum().amount#df.groupby(['shop_name','subcategory'])['amount'].sum()
    ser[ser!=0].to_excel('G:\\work files\\file'+str(inda)+'.xlsx',encoding='utf')#取值为不为0的项

        
if __name__=='__main__':
    main(sys.argv[1])
  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值