@[TOC] (目录)
0.基础快捷键
- 命令模式–>编辑模式,用enter,,反之,用esc
- 命令模式下:
- Y代码格式,M/m 是markdown格式
- 标题:直接敲1,2,3
- DD/dd 删除单元
- l/L 开关行号
- 在上方添加一行A,下方添加一行B,剪切选中的单元X,复制选中的单元C,粘贴到下方的单元V,粘贴到上方:shift+V
- Z恢复删除的最后一个单元
- 空格键:向下滚动,shift+空格:向上滚动
- Ctrl+shift±拆分cell
- shift+tab查看函数
- III强制终止cell(注意:这个快捷键是i),不影响前面运行过的内容!
1. 数据来源
- 1.1 直接导入
- .csv 文件 /.xlsx文件
# read_csv()
data = pd.read_csv(r"E:\test.csv",index_col=0,parse_dates=["入学时间"], date_parser=lambda x: datetime.strptime(x, "%Y/%m/%d"))
data
# read_csv()常用参数
sep:可以指定分隔符
delim_whitespace=True:表示分割符为空白字符,可以是空格、"\t"等等。
names和header:①不指定names,指定header=2,则第三行为标题②指定header=2,同时指定names=["x1","x2","x3","x4","x5"],则先是第三行为标题,后将标题换成了names内容③注意指定names时,heaer=0和不指定header=0的结果不同
若想更改列名,可通过header=0,names=[...]
总结:所以names和header的使用场景主要如下:
1. csv文件有表头并且是第一行,那么names和header都无需指定;
2. csv文件有表头、但表头不是第一行,可能从下面几行开始才是真正的表头和数据,这个时候指定header即可;
3. csv文件没有表头,全部是纯数据,那么我们可以通过names手动生成表头;
4. csv文件有表头、但是这个表头你不想用,这个时候同时指定names和header。先用header选出表头和数据,然后再用names将表头替换掉,其实就等价于将数据读取进来之后再对列名进行rename;
index_col:指定某列为索引,可以用列名或序号,如index_col="学号",index_col=0
usecols:只导入指定的列,若指定index_col也需要加入,可以配合lambda使用,其中x是列名,例子:①index_col="学号",usecols=["学号","姓名","性别"]②,index_col="学号",usecols=lambda x:x in ["学号","姓名","性别"]
dtype:指定某些列的数据类型,例子:①dtype={"学号":str}
converters:可以在读取的时候对列数据进行变换,在使用converters参数时,解析器默认所有列的类型为 str,所以需要显式类型转换。例子:将学号乘10,converters={"学号":lambda x:int(x)*10}
true_values和false_value:指定哪些值应该被清洗为True,哪些值被清洗为False。注意这里的替换规则,只有当某一列的数据全部出现在true_values + false_values里面,才会被替换。例子:某列的取值为“对、错”,则可用true_values=["对"], false_values=["错"]将字符转换成布尔型
skiprows:表示过滤行,想过滤掉哪些行,就写在一个列表里面传递给skiprows即可。注意的是:这里是先过滤,然后再确定表头,也可以用lambda,此时x指的是行索引。例子:①skiprows=[0]过滤掉第1行,②skiprows=lambda x: x > 0 and x % 2 == 0凡是索引大于0、并且%2等于0的记录都过滤掉
nrows:指定读入的行数,例子:①nrows=0只读入标题行,②nrows=3只读入前四行(包括标题行)
na_values参数:可以配置哪些值需要处理成 NaN。例子:① na_values=[10, 100]将df中所有[10, 100]变为Nan②针对列赋值:na_values={"年龄":10, "成绩":100}
keep_default_na=False:不将"-1.#IND"、"1.#QNAN"、"1.#IND"、"-1.#QNAN"、"#N/A N/A"、"#N/A"、"N/A"、"NA"、"#NA"、"NULL"、"NaN"、"-NaN"、"nan"、"-nan"、""转化为NaN,可以配合na_values 一块儿使用,只指定某些特殊字符为NaN.
parse_dates:指定某些列为时间类型,有事不能直接转化,需要结合date_parser指定字符格式,从而进行转化,例子:parse_dates=["入学时间"], date_parser=lambda x: datetime.strptime(x, "%Y/%m/%d"),此时要from datetime import datetime
thousands:指定千位符,例子:thousands=","
- 1.2 创建数据DF
- 列表或者数组形式
- 字典形式
# 1.列表
nums = [[i for i in range(3)] for _ in range(5)]#五行三列
col_name = [f'col_{i}' for i in range(3)] # 列名
row_name = [f'row_{i}' for i in range(5)] # 索引
pd.DataFrame(data=nums,index=row_name,columns=col_name)
# 2.字典(可以是数组、列表、序列)
a = np.random.randint(0,10,5)#[0,9]五个随机整数
b = np.random.randint(0,10,5)
row_name = [f'row_{i}' for i in range(5)] # 索引
pd.DataFrame(data={'a':a,'b':b},index=row_name)
2. 数据描述
- 2.1 常用属性
- shape/columns/index/dtypes/values
- 2.2 常用函数
- head() tail() sample() describe() info()
- 2.3 常用统计函数
- .mean(axis=…) .max .min .var .std .count .median .quantile
- .shift(n) 表示读取上n行的数据,若为负值则是读取下几行 .diff(-1)表示本行与下一行数据相减 .pct_change(n) 类似diff,求本行和上n行数值的比值,即本行/上n行
- .cumsum() 求该列的累加值, .cumprod()求该列的累乘值
- .value_counts(normalize=False,sort=True,ascending=False,bins=None,dropna=True),针对序列,其中sort设置结果是否排序,ascending默认降序,normalize若为True,表示以比例形式显示,bins对于数值数据可以用来分箱,dropna默认删除na值,可设置为True来统计na值数目。
- 2.4 几个常用函数
- 去重:df.drop_duplicates(subset=[‘a’,‘b’],keep=‘first’,inplace=True)其中subset选择列,keep表示保留上一行还是下一行,取值有first/last/False都删掉
- 转置:df.T
- 重命名:df.rename(columns={“a”:“axx”})列名重命名
- 判断DF是否为空:df.empty,例子:pd.DataFrame().empty输出True
3. 数据筛选
- 3.1 筛选出指定的几行/几列
- loc按标签值查找,iloc按数字位置查找 .loc[flag1,flag2],其中flag2可以为空,只筛选行,flag1可以为布尔类型,也可以设置条件,比如>=<!=以及isin,反操作是用~符号,
- 3.2 针对某列取值的筛选条件
- .isin():df.age.isin([…])
- .where():传入等长的布尔值进行筛选,将满足True的赋值为NaN或指定的其他值,例子:df.age.where(cond1&cond2,other=999,inplace=True),其中cond1和cond2中的判断条件可以为其他列哦。
- .mask():是.where的反操作
- .query():是一种非常优雅的查询方式,查询判断条件写在引号里,可以用模糊匹配、可以用@变量来引用变量。例子:df[df.age>10]相当于df.query(‘age>10’),或者df.query(“name.str.contains(‘j|w’)& age>10”)
- .filter():用于判断列名(axis=1)和索引(axis=0)是否满足条件,参数regex正则,like='j’模糊匹配包含j的
- 3.3 模糊匹配
- .str.contains():类似sql里的Like,例子:df.name.str.contains(‘j|w’)查找name中包括j或者w的数据。可以用正则
- .str.startswith():查找以…开头
- .str.endswith(): 查找以…结尾
- 3.4 空值/缺失值处理(定位、删除、补全)
- isna()/isnull()/notna()/notnull():nan->not a number一般是数值的null,isnull()可以判断所有的空值,python中二者一样
- pandas中判断:df.isna()/.isnull()可判断所有类型;numpy中判断:np.isnan()–判断数值型,np.isnat()–判断时间类型。
- 计算缺失值占比:a.isnull().sum()/len(a)
- 删除缺失值:.dropna(axis=0,how=‘any’,thresh=None,subset=None,inplace=Fase)其中axis=0表示按行删除,how='any/all’若为all表示整行都是nan的话,才将其删除,any表示行里包含nan则删除行,thresh是Int类型,表示保留含有int个非nan值的行,subset=[“a”,…]取值是列名,若所选列中含有nan,则删除这些列中含有nan的行(此时axis=0,how=any)
- 填补缺失值:fillna(value=None,method=None,axis=0,limit=None,inplace=False),其中method=‘pad/ffill/backfill/bfill/None’,pad/ffill用前一个非缺失值去填充,backfill/bfill用下一个非缺失值填充,None用指定的值填充,limit限制填充个数,axis=0是以行为单位,value可以是固定值,也可以为字典,key指列名,limit是Int,限制填充的NAN的连续个数
- 3.5 排序
- Series排序:s.sort_values(ascending=True,inplace=False)
- DF排序:df.sort_values(by,ascending=True,inplace=False),如果是多列排序,比如:df.sort_values(by=[‘a’,‘b’],ascending=[True,False])
- rank():排名,参数ascending设置是否升序,pct=True设置排名以比例输出,method=‘average/min/max/first/dense’,其中average为默认,是指在相等的分组中,为各值分配平均排名,min(此种一般用于成绩排名)/max是指在整个分组的最小/最大排名,first是按值在原始数据中的出现顺序分配排名,dense是将排名压缩按1,2…顺序;na_option=‘keep/top/bottom’,其中keep为默认,表示忽略nan,top表示将nan值作为最小值(升序时)计入排序,bottom表示将nan值作为最大值(升序时)计入排序
4. 数据拼接
- 4.1 append
- 首先注意pandas即将取消append这种方法,建议用concat;
- 是多个表纵向拼接的方法:new_df=df1.append([df2,df3],ignore_index=False,verify_integrity=False)其中ignore_index是对行索引重组,比如以前两个表有各自的索引,可能有重复的,ignore_index=True时,会重新设置索引;verify_integrity=True是检查合并后的索引有无重复,若有重复则报错!
- 4.2 merge
- 是横向拼接的方式:pd.merge(left,right,how=‘inner’,on=None,left_on=None,right_on=None,left_index=False,right_index=False,suffixes=‘_x’,‘_y’,copy=True)其中how可取inner/left/right/outer,left_on是左表用于连接的列名,left_index指是否用左表的行索引作为连接键,注意on和index只能用一个。
- 注意:以左连接为例,若左表有n个重复键,右表有m个重复键,那么对于重复建将生成n*m个
- 4.3 join
- 与merge类似,但没有merge完善,建议用merge即可
- df1.join(df2,how=“…”)
- 4.4 concat
- 可以横向拼接axis=1,也可以纵向拼接axis=0
- pd.concat([df1,df2,…],axis=0,join=‘outer’,keys=None,ignore_index=False,verify_integrity=False)其中join=inner表示去交集,outer表示取并集,keys可以自己设置键表示多个表,比如做两表纵向拼接时,可用keys=[‘x’,‘y’]则主键会多一列标记x,y。
5. 聚合和分组
- 5.1 分组groupby
- groupby(by,axis=0,as_index=True,sort=True)其中axis默认按行切分,as_index指是否将分组列名作为索引,若为False相当于加了reset_index功能,sort默认按分组列名排序。。这个by还有几种输入形式:①列名②单列字段的转换格式,例子:df.groupby(df[‘姓名’].str[0]).mean()表示按“姓氏”进行分组。③字典映射。per={0:‘男’,1:‘女’},df.groupby(per).mean()就是按照男女进行分组。④lambda函数,df.groupby(lambda x:‘男’ if x==0 else ‘女’).mean()
- 5.2 .groupby().agg()
- :agg()每个分组得到一个值,agg()中可传入字典,比如:df.groupby([班级,姓名]).agg({语文:[np.mean,min],数学:[max]})表示计算各班每位学生的语文平均分和最低分,数学最高分
- 5.3 .groupby().transform()
- 对每个分组计算,但是对所有行输出相同值
- 5.4 .gruopby().apply()
- 此时apply中将传入分组的df,可自定义函数对df进行处理,返回值是每个分组的拼接值
- 5.5 其他
- 可以将分组后的结果做merge拼接哦
6. pandas三大利器
- 6.1 apply
- 功能最全!!贼好用!!
- df.apply(func,args=(1,),axis=0)其中axis=0表示传入函数的是按列来的,一列一列的处理,一列series传入func中,func还可以有其他参数,具体值通过args设置。可参考https://zhuanlan.zhihu.com/p/100064394
- apply对行处理的时候,可用[]判断
- 6.2 map
- map功能类似apply,但是不能传入额外参数args
- map另一个功能就是可以直接映射赋值,比如:df.gender.map({“男”:0,“女”:1})
- 6.3 applymap
- 针对df中的每个单元格指定函数操作,比如将所有值保留两位小数:df.applymap(lambda x:“%.2f”%x)
7. 时间类型
- 7.1 判断是字符串还是时间类型
- type(df[‘a’][0])
- 7.2 字符串和时间类型的相互转换
- 字符串->时间类型:①df[“a”]=pd.to_datetime(df[“a”]);②使用datetime包中的strptime():datetime.datetime.strptime(s,‘%Y-%m-%d’)'%Y-%m-%d’要和字符串的形式一致
- 时间类型->字符串:①直接使用str(…);②使用datetime包中的strftime(),datetime.datetime.strftime(d,‘%Y-%d’)此时’%Y-%d’与想要得到的字符串格式一致。
- 7.3 生成日期序列
- pd.date_range(‘1/1/2022’,periods=24,freq=‘D’)连续生成24期,按天;freq='M’是按月,每月月末;H为小时,T为分钟,S为秒,得到DatetimeIndex,转换成series可用.to_series();也可以通过指定start/end,具体参考https://pandas.pydata.org/docs/reference/api/pandas.date_range.html
- pd.period_range(‘1/1/2022’,periods=24,freq=‘D’)结果与上述类似,只不过是PeriodIndex
- 7.4 日期加减
- 日期加一天:①可以直接使用pd.Timedelta(days=1),注意Timedelta函数里没有年和月,周-weeks,天-days-D,小时-hours-h,分-minutes-m;②也可用datetime.date.today()+datetime.timedelta(days=1);
- 日期加一个月:需要第三方库:from dateutil.relativedelta import relativedelta;datetime.datetime.now() + relativedelta(months=+1)
- 日期之差:直接相减得到date_interval;①date_interval.days得到相差天数
- 7.5 Timestamp类的常用属性
- 将str转换为日期后,都可以使用如下属性
- year/month/day/hour/minute/second/date/time/week/quarter/weekofyear一年中第几周/dayofyear一年第几天/dayofweek一周第几天/weekday一周第几天/weekday_name星期名称/is_leap_year是否闰年
- 7.6 Series.dt返回dt对象
- dt的一些属性dt.year/.month/…/.is_leap_year/.is_year_end是否是年底最后一天/is_month_end/days_in_month这一天是这月中的第几天
8.字符串类型
print(df['股票代码'])
print('sz000002'[:2])
print(df['股票代码'].str[:2])
print(df['股票代码'].str.upper()) # 加上str之后可以使用常见的字符串函数对整列进行操作
print (df['股票代码'].str.lower())
print (df['股票代码'].str.len()) # 计算字符串的长度,length
df['股票代码'].str.strip() # strip操作,把字符串两边的空格去掉
print(df['股票代码'].str.contains('sh')) # 判断字符串中是否包含某些特定字符
print(df['股票代码'].str.replace('sz', 'sh')) # 进行替换,将sz替换成sh
split操作
print (df['新浪概念'].str.split(';')) # 对字符串进行分割
print(df['新浪概念'].str.split(';').str[:2]) # 分割后取第一个位置
print(df['新浪概念'].str.split(';', expand=True)) # 分割后并且将数据分列
9.其他有趣的操作
-
9.1 多列合并为一列
- df[‘ColumnA’] = df[df.columns[1:]].apply(lambda x: ‘,’.join(x.dropna()),axis=1);其中 ”1:“表示合并第一列之后的各列为一列
-
9.2 一行变多行/一列变多列
- 查看https://blog.csdn.net/kelanj/article/details/124117687?spm=1001.2014.3001.5502
- 一列变多列就是上述操作不用.stack()的效果
-
9.3 rolling和expanding