其实我一点儿都不想用pandas的dataframe,总感觉这个东西很难用,也许是我的水平还没达到。但是好多时候读写实验室的excel和输出成excel又必须用这个,之前用的seaborn也是基于pandas的,那就再把基础操作复习一下。
读写与创建
数据读写
sql
1 2 3 4 5 import pandas as pdfrom sqlalchemy import create_enginedb = create_engine("mysql+pymysql://用户名:用户密码@localhost:端口号(3306)/使用的数据库名?charset=utf8" ) sql = "select * from text" df = pd.read_sql(sql, db, index_col="index" )
1 2 3 4 sql.to_sql(df, name='test' , con=db, if_exists="append" , index=False )
excel
1 2 3 4 5 6 7 df = pd.read_excel(r'file_path' , sheet_name='指定sheet,默认第一个' , index=False , index_col=0 , header=0 , usecols=[0 , 2 ] )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ''' 按照不同sheet写入 ''' excelWriter = pd.ExcelFile('file_path/test.xlsx' ) df.to_excel( excelWriter, sheet_name='' , insex=False , encoding='utf-8' , columns=['a' , 'b' ], na_rep=0 , inf_rep=0 , ) excelWriter.save() ''' 直接写入 ''' df.to_excel('file_path/test.xlsx' )
csv
1 2 3 4 5 6 7 8 9 10 df = pd.read( r'file_path/test.csv' , sep="" , nrows=2 , encoding='utf-8' , engine='python' , usecols=[0 , 2 ], index_col=0 , header=0 )
1 2 3 4 5 6 7 8 9 df.to_csv( r'file_path/test.csv' , index=False , columns=['a' , 'b' ], sep=',' , na_rep=0 , encoding='utf-8' , )
txt
1 pd.read_table(r'file_path/test.txt' , sep='' )
1 2 3 4 5 6 7 8 9 df.to_csv( r'file_path/test.csv' , index=False , columns=['a' , 'b' ], sep=',' , na_rep=0 , encoding='utf-8' , )
来源https://www.modb.pro/db/26894
创建
最让我不爽的就是dataframe没有像np.zeros,np.ones这种根据已有的dataframe来初始化一个空dataframe.
1 df_empty=pd.Dataframe(columns=['A' ,'B' ,'C' ,'D' ])
所以有一种办法就是把已有的datafram列名提取出来,然后再去创建。
索引
Pandas 数据的索引就像一本书的目录,让我们很快地找到想要看的章节,作为大量数据,创建合理的具有业务意义的索引对我们分析数据至关重要。
认识索引
下图是一个简单的 DataFrame 中索引的示例:
pandas index
其中:
行索引是数据的索引,列索引指向的是一个 Series
DataFrame 的索引也是系列形成的 Series 的索引
建立索引让数据更加直观明确,如每行数据是针对一个国家的
建立索引方便数据处理
索引允许重复,但业务上一般不会让它重复
有时一个行和列层级较多的数据会出现多层索引 的情况。
建立索引
之前我们学习了加载数据生成 DataFrame 时可以指定索引
1 2 3 4 5 6 7 8 9 10 11 12 data = 'https://www.gairuo.com/file/data/dataset/team.xlsx' df = pd.read_excel(data, index_col='name' ) df ''' team Q1 Q2 Q3 Q4 name Liver E 89 21 24 64 Arry C 36 37 37 57 Ack A 57 60 18 84 Eorge C 93 96 71 78 Oah D 65 49 61 86 '''
如果加载时没有指定索引,我们可以使用 df.set_index()
指定:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 df.set_index('month' ) df.set_index(['month' , 'year' ]) ''' sale month year 1 2012 55 4 2014 40 2013 84 10 2014 31 ''' s = pd.Series([1 , 2 , 3 , 4 ]) df.set_index(s) df.set_index([s, 'year' ]) df.set_index([s, s**2 ]) df.set_index('month' , drop=False ) df.set_index('month' , append=True ) df.set_index('month' , inplace=True )
重置索引
有时我们想取消已有的索引,以重新来过,可以使用 df.reset_index()
:
1 2 3 4 5 6 7 8 9 10 11 df.reset_index() df.set_index('month' ).reset_index() df.set_index('month' ).reset_index(drop=True ) df2.reset_index(inplace=True ) df.set_index(['month' , 'year' ]).reset_index(level=1 ) df2.reset_index(level='class' ) df.reset_index(level='class' , col_level=1 ) df.reset_index(level='class' , col_level=1 , col_fill='species' )
索引类型
为了适应各种业务数据的处理,索引又针对各种类型数据定义了不同的索引类型:
数字索引 Numeric Index
共有以下几种:
RangeIndex: 单调整数范围的不可变索引。
Int64Index: int64类型,有序可切片集合的不可变ndarray。
UInt64Index: 无符号整数标签的
Float64Index: Float64 类型
1 2 3 4 5 6 7 8 pd.RangeIndex(1 ,100 ,2 ) pd.Int64Index([1 ,2 ,3 ,-4 ], name='num' ) pd.UInt64Index([1 ,2 ,3 ,4 ]) pd.Float64Index([1.2 ,2.3 ,3 ,4 ])
类别索引 CategoricalIndex
类别只能包含有限数量的(通常是固定的)可能值(类别)。 可以理解成枚举,比如性别只有男女,但在数据中每行都有,如果按文本处理会效率不高。类别的底层是 pandas.Categorical。
1 2 pd.CategoricalIndex(['a' , 'b' , 'a' , 'b' ])
类别后边后有专门的讲解,只有在体量非常大的数据面前才能显示其优势。
间隔索引 IntervalIndex
1 2 3 4 5 6 pd.interval_range(start=0 , end=5 ) ''' IntervalIndex([(0, 1], (1, 2], (2, 3], (3, 4], (4, 5]], closed='right', dtype='interval[int64]') '''
多层索引 MultiIndex
教程后边会有专门的讲解。
1 2 3 4 5 6 7 8 9 arrays = [[1 , 1 , 2 , 2 ], ['red' , 'blue' , 'red' , 'blue' ]] pd.MultiIndex.from_arrays(arrays, names=('number' , 'color' )) ''' MultiIndex([(1, 'red'), (1, 'blue'), (2, 'red'), (2, 'blue')], names=['number', 'color']) '''
时间索引 DatetimeIndex
1 2 3 4 5 6 7 8 9 pd.date_range(start='1/1/2018' , end='1/08/2018' ) pd.date_range(start='1/1/2018' , periods=8 ) pd.period_range(start='2017-01-01' , end='2018-01-01' , freq='M' ) pd.period_range(start=pd.Period('2017Q1' , freq='Q' ), end=pd.Period('2017Q2' , freq='Q' ), freq='M' )
时间差 TimedeltaIndex
1 2 3 4 5 6 7 pd.TimedeltaIndex(data =['06:05:01.000030' , '+23:59:59.999999' , '22 day 2 min 3us 10ns' , '+23:29:59.999999' , '+12:19:59.999999' ]) pd.TimedeltaIndex(['1 days' , '1 days, 00:00:05' , np.timedelta64(2 , 'D' ), datetime.timedelta(days=2 , seconds=2 )])
周期索引 PeriodIndex
1 2 t = pd.period_range('2020-5-1 10:00:05' , periods=8 , freq='S' ) pd.PeriodIndex(t,freq='S' )
索引对象
行和列的索引在 Pandas 里其实是一个 Index
对象,以下是创建一个 index
对象的方法:
创建对象
1 2 3 4 5 6 pd.Index([1 , 2 , 3 ]) pd.Index(list ('abc' )) pd.Index(['e' , 'd' , 'a' , 'b' ], name='something' )
查看
属性
以下方法也适用于 df.columns
, 因为都是 index 对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 df.index.name df.index.array df.index.dtype df.index.shape df.index.size df.index.values df.index.empty df.index.is_unique df.index.names df.index.is_all_dates df.index.has_duplicates df.index.values
操作
以下方法也适用于 df.columns
, 因为都是 index 对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 df.index.astype('int64' ) df.index.isin() df.index.rename('number' ) df.index.nunique() df.index.sort_values(ascending=False ,) df.index.map (lambda x:x+'_' ) df.index.str .replace('_' , '' ) df.index.str .split('_' ) df.index.to_list() df.index.to_frame(index=False , name='a' ) df.index.to_series() df.index.to_numpy() df.index.unique() df.index.value_counts() df.index.where(df.index=='a' ) df.index.rename('grade' , inplace=False ) df.index.rename(['species' , 'year' ]) df.index.max () df.index.argmax() df.index.any () df.index.all () df.index.T df.index.append(pd.Index([4 ,5 ])) df.index.repeat(2 ) df.index.inferred_type df.index.hasnans df.index.is_monotonic_decreasing df.index.is_monotonic df.index.is_monotonic_increasing df.index.nbytes df.index.ndim df.index.nlevels df.index.min () df.index.argmin() df.index.argsort() df.index.asof(2 ) df.index.astype('int64' , copy=True ) df.index.copy(name='new' , deep=True , dtype='int64' ) df.index.delete(1 ) df.index.difference(pd.Index([1 ,2 ,4 ]), sort=False ) df.index.drop('a' , errors='ignore' ) df.index.drop_duplicates(keep='first' ) df.index.droplevel(0 ) df.index.dropna(how='all' ) df.index.duplicated(keep='first' ) df.index.equals(df.index) df.index.factorize() df.index.fillna(0 , {0 :'nan' }) df.index.format (name=True , formatter=lambda x:x+10 ) df.index.get_indexer([2 ,9 ]) df.index.get_level_values(0 ) df.index.get_loc('b' ) df.index.insert(2 , 'f' ) df.index.intersection(df.index) df.index.is_(df.index) df.index.is_categorical() df.index.is_type_compatible(df.index) df.index.is_type_compatible(1 ) df.index.isna() df.index.isnull() df.index.join(df.index, how='left' ) df.index.notna() df.index.notnull() df.index.ravel() df.index.reindex(['a' ,'b' ]) df.index.searchsorted('f' ) df.index.searchsorted([0 , 4 ]) df.index.set_names('quarter' ) df.index.set_names('species' , level=0 ) df.index.set_names(['kind' , 'year' ], inplace=True ) df.index.shift(10 , freq='D' ) idx1.symmetric_difference(idx2) idx1.union(idx2) df.add_prefix('t_' ) df.add_suffix('_d' ) df.first_valid_index() df.last_valid_index()
索引重命名
对行和列的索引名进行修改。
1 2 3 4 5 6 7 8 9 10 11 df.rename(columns={"A" : "a" , "B" : "c" }) df.rename(str .lower, axis='columns' ) df.rename(index={0 : "x" , 1 : "y" , 2 : "z" }) df.rename({1 : 2 , 2 : 4 }, axis='index' ) df.rename(index=str ) replacements = {l1:l2 for l1, l2 in zip (list1, list2)} df.rename(replacements)
索引名重命名
注意,这是修改索引的名称,不是索引或者列名本身:
1 2 3 4 5 6 7 8 s.rename_axis("animal" ) df.rename_axis(["dow" , "hr" ]) df.rename_axis('info' , axis="columns" ) df.rename_axis(index={'a' : 'A' , 'b' : 'B' }) df.rename_axis(columns={'name' : 's_name' , 'b' : 'B' }) df.rename_axis(columns=str .upper)
部分示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 idx = pd.Index([1 ,2 ,3 ]) df.index.isin(idx) df.index.isin(['a' ,'b' ]) midx = pd.MultiIndex.from_arrays([[1 ,2 ,3 ], ['red' , 'blue' , 'green' ]], names=('number' , 'color' )) midx.isin([(1 , 'red' ), (3 , 'red' )]) dates = ['2000-03-11' , '2000-03-12' , '2000-03-13' ] dti = pd.to_datetime(dates) dti.isin(['2000-03-11' ]) idx = pd.Index(['b' , 'a' , 'd' , 'c' ]) order = idx.argsort() idx[order] idx2 = pd.Index([1 ,3 ,6 ]) idx2.asof(5 ) idx2.asof(6 ) idx2.asof(-1 ) unique_index = pd.Index(list ('abc' )) unique_index.get_loc('b' ) monotonic_index = pd.Index(list ('abbc' )) monotonic_index.get_loc('b' ) non_monotonic_index = pd.Index(list ('abcb' )) non_monotonic_index.get_loc('b' )
查询与修改
数据检查
我们一拿到数据需要对数据有一个抽查,一方面是了解数据结构,另一方面随机检查一下数据的质量问题。常用的:
df.head(n)
查看 DataFrame 对象的前n行
DataFrame
df.tail(n)
查看 DataFrame 对象的最后n行
DataFrame
df.sample(n)
查看 n 个样本,随机
DataFrame
以上都是选择整行。
查看头部 df.head()
每次加载数据后一般需要看一下头部数据
1 2 3 4 5 6 7 8 9 10 11 df.head() out: name team Q1 Q2 Q3 Q4 0 Liver E 89 21 24 64 1 Arry C 36 37 37 57 2 Ack A 57 60 18 84 3 Eorge C 93 96 71 78 4 Oah D 65 49 61 86 df.head(15 )
查看尾部 df.tail()
查看最后的尾部数据。
1 2 3 4 5 6 7 8 9 10 11 df.head() out: name team Q1 Q2 Q3 Q4 95 Gabriel C 48 59 87 74 96 Austin7 C 21 31 30 43 97 Lincoln4 C 98 93 1 20 98 Eli E 11 74 58 91 99 Ben E 21 43 41 74 df.tail(15 )
查看样本 df.sample()
df.sample()
会随机返回一条样本数据。
1 2 3 4 5 6 7 df.sample() out: name team Q1 Q2 Q3 Q4 79 Tyler A 75 16 44 63 df.sample(15 )
数据截取:
1 2 3 4 df.truncate(before=2 , after=4 ) s.truncate(before="60" , after="66" ) df.truncate(before="A" , after="B" , axis="columns" )
操作列
以下两种方法都可以代表一列:
1 2 3 4 5 df['name' ] df.name df.Q1
注意,当列名为一个合法的 python 变量时可以直接作为属性去使用。
选择行列部分
有时我们需要按条件选择部分列、部分行,一般常用的有:
选择列
df[col]
Series
按索引选择行
df.loc[label]
Series
按数字索引选择行
df.iloc[loc]
Series
使用切片选择行
df[5:10]
DataFrame
用表达式筛选行
df[bool_vec]
DataFrame
接下来我们将重点介绍一下这些查询的方法。
切片 []
我们可以像列表那样利用切片功能选择部分行的数据,但是不支持索引一条:
1 2 3 4 5 df[:2 ] df[4 :10 ] df[:] df[:10 :2 ] s[::-1 ]
也可以选择列:
1 2 3 df['name' ] df[['Q1' , 'Q2' ]] df[['name' ]]
按标签 .loc
df.loc()
的格式为 df.loc[<索引表达式>, <列表达式>],表达式支持以下形式:列表达式>索引表达式>
单个标签:
单个列表标签:
1 2 3 4 df.loc[[0 ,5 ,10 ]] df.loc[['Eli' , 'Ben' ]] df.loc[[False , True ]*50 ]
带标签的切片(包括起始和停止):
1 2 3 4 df.loc[0 :5 ] df.loc['2010' :'2014' ] df.loc[:]
列筛选,必须有行筛选:
1 2 3 dft.loc[:, ['Q1' , 'Q2' ]] dft.loc[:, ['Q1' , 'Q2' ]] dft.loc[:10 , 'Q1' :]
按位置 .iloc
df.iloc
与 df.loc
相似,但只能用自然索引(行和列的 0 - n 索引),不能用标签。
1 2 3 4 df.iloc[:3 ] df.iloc[:] df.iloc[2 :20 :3 ] s.iloc[:3 ]
取具体值 .at
类似于 loc, 但仅取一个具体的值,结构为 at[<索引>,<列名>]:列名>索引>
1 2 3 4 5 6 7 8 9 10 df.at[4 , 'Q1' ] df.at['lily' , 'Q1' ] df.at[0 , 'name' ] df.loc[0 ].at['name' ] df.set_index('name' ).at['Eorge' , 'team' ] df.set_index('name' ).team.at['Eorge' ] df.team.at[3 ]
同样 iat 和 iloc 一样,仅支持数字索引:
1 2 df.iat[4 , 2 ] df.loc[0 ].iat[1 ]
.get 可以做类似字典的操作,如果无值给返回默认值(例中是0):
1 2 3 4 df.get('name' , 0 ) df.get('nameXXX' , 0 ) s.get(3 , 0 ) df.name.get(99 , 0 )
表达式筛选
[]
切片里可以使用表达式进行筛选:
1 2 3 4 5 6 7 df[df['Q1' ] == 8 ] df[~(df['Q1' ] == 8 )] df[df.name == 'Ben' ] df.loc[df['Q1' ] > 90 , 'Q1' :] df.loc[(df.Q1 > 80 ) & (df.Q2 < 15 )] df.loc[(df.Q1 > 90 ) | (df.Q2 < 90 )] df[df.Q1 > df.Q2]
df.loc
里的索引部分可以使用表达式进行数据筛选。
1 2 3 4 5 6 7 8 df.loc[df['Q1' ] == 8 ] df.loc[df.Q1 == 8 ] df.loc[df['Q1' ] > 90 , 'Q1' :] df.loc[:, lambda df: df.columns.str .len ()==4 ] df.loc[:, lambda df: [i for i in df.columns if 'Q' in i]] df.iloc[:3 , lambda df: df.columns.str .len ()==2 ]
逻辑判断和函数:
1 2 3 4 5 6 7 8 9 df.eq() df.ne() df.le() df.lt() df.ge() df.gt() df[df.Q1.ne(89 )] df.loc[df.Q1.gt(90 ) & df.Q2.lt(90 )]
其他函数:
1 2 3 df[df.team.isin(['A' ,'B' ])] df[df.isin({'team' : ['C' , 'D' ], 'Q1' :[36 ,93 ]})]
函数筛选
1 2 df[lambda df: df['Q1' ] == 8 ] df.loc[lambda df: df.Q1 == 8 , 'Q1' :'Q2' ]
where 和 mask
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 s.where(s > 90 ) s.where(s > 90 , 0 ) np.where(s>80 , True , False ) np.where(df.num>=60 , '合格' , '不合格' ) s.mask(s > 90 ) s.mask(s > 90 , 0 ) m = df.loc[:,'Q1' :'Q4' ] % 3 == 0 df.loc[:,'Q1' :'Q4' ].where(m, -df.loc[:,'Q1' :'Q4' ]) df.lookup([1 ,3 ,4 ], ['Q1' ,'Q2' ,'Q3' ]) df.lookup([1 ], ['Q1' ])
query
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 df.query('Q1 > Q2 > 90' ) df.query('Q1 + Q2 > 180' ) df.query('Q1 == Q2' ) df.query('(Q1<50) & (Q2>40) and (Q3>90)' ) df.query('Q1 > Q2 > Q3 > Q4' ) df.query('team != "C"' ) df.query('team not in ("E","A","B")' ) df.query('B == `team name`' ) a = df.Q1.mean() df.query('Q1 > @a+40' ) df.query('Q1 > `Q2`+@a' ) df[df.eval ("Q1 > 90 > Q3 > 10" )] df[df.eval ("Q1 > `Q2`+@a" )]
filter
使用 filter 可以对行名和列名进行筛选。
1 2 3 4 5 6 7 df.filter (items=['Q1' , 'Q2' ]) df.filter (regex='Q' , axis=1 ) df.filter (regex='e$' , axis=1 ) df.filter (regex='1$' , axis=0 ) df.filter (like='2' , axis=0 ) df.filter (regex='^2' , axis=0 ).filter (like='Q' , axis=1 )
上面两个来自于https://www.gairuo.com/
合并与新增行列
新增列
假设原始数据如下:
1 2 3 4 5 6 import pandas as pdimport numpy as npdf = pd.DataFrame({'num_legs' : [4 , 2 ], 'num_wings' : [0 , 2 ]}, index=['dog' , 'hawk' ]) slen = len (df['num_legs' ])
1)直接赋值
1 2 3 4 5 6 7 8 9 df['a' ] = pd.Series(np.random.randn(slen), index=df.index) df['b' ] = None df['c' ] = [2 , 4 ] df['c1' ] = ['no' , 'yes' ] df.index = [1 , 0 ] df['c2' ] = pd.Series(['no' , 'yes' ]) df['c3' ] = pd.Series(['no' , 'yes' ], index=df.index)
2)loc方法
1 2 df.loc[:,'d' ] = pd.Series(np.random.randn(slen), index=df.index) df.loc[:, 'd' ] = [2 , 4 ]
3)insert方法
insert方法使用的列名不能有重复值,连更新都不能
1 2 df.insert(len (df.columns), 'e' , pd.Series(np.random.randn(slen)), index=df.index) df.insert(len (df.columns), 'ee' , [1 ,2 ])
4)assign方法
assign方法参数可以是Series、标量、列表,还可以同时添加多列
1 2 df = df.assign(f=df.num_wings.mean()) df = df.assign(A=df.num_wings.sum (), B=[1 ,2 ])
5)concat方法
1 pd.concat([df, pd.Series(['yes' , 'yes' ]).rename('t' )], axis=1 )
注意点:
每个方法的参数都可以是Series、标量、列表
insert方法中新增的列名不能跟已有的一样,即使更新刚刚新增的列也会出错
df['a']=pd.Series(['no', 'yes']
的index顺序如果被修改,默认是以Series的index为准,可以通过index=df.index
来指定按照原始DataFrame的index顺序
新增行
1 2 3 4 5 import pandas as pdimport numpy as npdf = pd.DataFrame(columns=['lib' , 'qty1' , 'qty2' ])
1)使用loc
1 2 3 for i in range (4 ): df.loc[i] = [np.random.randint(-1 , 1 ) for n in range (3 )]
2)使用append
1 2 3 4 5 df.append({'lib' : 2 , 'qty1' : 3 , 'qty2' : 4 }, ignore_index=True ) df2 = pd.DataFrame([[1 ,2 ,3 ], [2 ,3 ,4 ]], columns=['lib' , 'qty1' , 'qty2' ]) df.append(df2, ignore_index=True )
3)重新生成DataFrame
循环将要添加的数据以字典的形式保存到一个列表中,在用列表创建出DataFrame
1 2 3 4 5 6 row_list = [] input_rows = [[1 ,2 ,3 ], [2 ,3 ,4 ]] for row in input_rows: dict1 = dict (lib=row[0 ], qty1=row[1 ], qty2=row[2 ]) row_list.append(dict1) df = pd.DataFrame(row_list)
以上两个来源于https://amberwest.github.io/
合并
append() Vs. concat()
连接或者合并DataFrame的时候一般有两种方式:纵向和横向。听起来总是觉得有点迷迷糊糊的。通俗的解释就是,纵向就是把两个或多个DataFrame纵向(从上到下)连接到一个DataFrame当中,index和column有重复情况也不进行任何操作,就是粗暴的纵向拼接DataFrame。横向就是会考虑如果有相同的index的话就会把相同index上所有列的数据合并在一起了,简单点理解就是相当于使用Excel中的V-lookup在两张有相同id但不同数据的表中进行了数据的融合。连接与合并DataFrame的常用函数有两个append()
,concat()
还有merge()
。其中append()只能进行纵向连接,而concat()
和merge()
可以进行both。concat()
默认是进行纵向连接,也就是跟append()
效果一样,如果想要使用concat()
进行横向合并则需要在concat()
中声明变量axis。默认值:concat(axis=0)
纵向连接,concat(axis=1)
横向合并。下面举几个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 In [1 ]: population = pd.read_csv('population_00.csv' , index_col=0 ) In [1 ]: unemployment = pd.read_csv('unemployment_00.csv' , index_col=0 ) In [1 ]: print (population) | | 2010 Census Population | |---------------|------------------------| | Zip Code ZCTA | | | 57538 | 322 | | 59916 | 130 | | 37660 | 40038 | | 2860 | 45199 | In [4 ]: print (unemployment) | | unemployment | participants | |-------|--------------|--------------| | Zip | | | | 2860 | 0.11 | 34447 | | 46167 | 0.02 | 4800 | | 1097 | 0.33 | 42 | | 80808 | 0.07 | 4310 |
以上为两个数据文件中数据的情况,下面讲举例说明append()和concat(axis=0)默认值对DataFrame纵向连接的结果,两种方式得到的结果是完全相同的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 In [5 ]: population.append(unemployment) Out[5 ]: | | 2010 Census Population participants unemployment | participants | unemployment | |-------|--------------------------------------------------|--------------|--------------| | 57538 | 322.0 | NaN | NaN | | 59916 | 130.0 | NaN | NaN | | 37660 | 40038.0 | NaN | NaN | | 2860 | 45199.0 | NaN | NaN | | 2860 | NaN | 34447.0 | 0.11 | | 46167 | NaN | 4800.0 | 0.02 | | 1097 | NaN | 42.0 | 0.33 | | 80808 | NaN | 4310.0 | 0.07 | In [6 ]: pd.concat([population, unemployment], axis=0 ) Out[6 ]: | | 2010 Census Population participants unemployment | participants | unemployment | |-------|--------------------------------------------------|--------------|--------------| | 57538 | 322.0 | NaN | NaN | | 59916 | 130.0 | NaN | NaN | | 37660 | 40038.0 | NaN | NaN | | 2860 | 45199.0 | NaN | NaN | | 2860 | NaN | 34447.0 | 0.11 | | 46167 | NaN | 4800.0 | 0.02 | | 1097 | NaN | 42.0 | 0.33 | | 80808 | NaN | 4310.0 | 0.07 |
这里我们可以看到zip邮编下的”2860”出现了两次。如果我们想把相同zip下两个DataFrame的数据信息合并,我们就得用到横向合并,concat()提供了一个非常方便的办法就是concat(axis=1)或者concat(axis=’columns’)就可以实现横向合并了:
1 2 3 4 5 6 7 8 9 10 11 In [7 ]: pd.concat([population, unemployment], axis=1 ) Out[17 ]: | | 2010 Census Population participants unemployment | participants | unemployment | |-------|--------------------------------------------------|--------------|--------------| | 1097 | NaN | 0.33 | 42.0 | | 2860 | 45199.0 | 0.11 | 34447.0 | | 37660 | 40038.0 | NaN | NaN | | 46167 | NaN | 0.02 | 4800.0 | | 57538 | 322.0 | NaN | NaN | | 59916 | 130.0 | NaN | NaN | | 80808 | NaN | 0.07 | 4310.0 |
concat() Vs. merge()
在上面说完了concat()
和append()
横向纵向的连接与合并之后,下面要说一下concat()
和merge()
的区别和关系。上面我们说了concat()
和merge()
都可以进行横纵向的合并,在用法上和输出结果上两者有一些区别。这里要引入join的概念。concat()
的默认join方式是outer join,而merge()
的默认join方式是inner join。另外concat()
和merge()
在合并DataFrame的时候还有一个重要的区别就是,concat()
是通过index来合并的,而merge()
是通过列明(column label )来合并的,如果列名设置成为了index的话需要把用来合并列名的index去掉之后再进行合并,否则会出现KeyError错误提示找不到列名。下面继续使用population和unemployment两个DataFrame来进行相关展示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 In [1 ]: population = pd.read_csv('population_00.csv' , index_col=0 ) In [1 ]: unemployment = pd.read_csv('unemployment_00.csv' , index_col=0 ) In [1 ]: print (population) | | 2010 Census Population | |---------------|------------------------| | Zip Code ZCTA | | | 57538 | 322 | | 59916 | 130 | | 37660 | 40038 | | 2860 | 45199 | In [2 ]: print (unemployment) | | unemployment | participants | |-------|--------------|--------------| | Zip | | | | 2860 | 0.11 | 34447 | | 46167 | 0.02 | 4800 | | 1097 | 0.33 | 42 | | 80808 | 0.07 | 4310 | In [3 ]: pd.concat([population, unemployment], axis=1 ) Out[3 ]: | | 2010 Census Population participants unemployment | participants | unemployment | |-------|--------------------------------------------------|--------------|--------------| | 1097 | NaN | 0.33 | 42.0 | | 2860 | 45199.0 | 0.11 | 34447.0 | | 37660 | 40038.0 | NaN | NaN | | 46167 | NaN | 0.02 | 4800.0 | | 57538 | 322.0 | NaN | NaN | | 59916 | 130.0 | NaN | NaN | | 80808 | NaN | 0.07 | 4310.0 | In [4 ]: pd.concat([population, unemployment], axis=1 , join='inner' ) Out[4 ]: | | 2010 Census Population participants unemployment | participants | unemployment | |-------|--------------------------------------------------|--------------|--------------| | 2860 | 45199.0 | 0.11 | 34447.0 |
接下来是对相同df进行merge操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 In [5 ]: population = pd.read_csv('population_00.csv' , index_col=0 ) In [5 ]: unemployment = pd.read_csv('unemployment_00.csv' , index_col=0 ) In [5 ]: pd.merge(population, unemployment, left_on='ZipCode' , right_on='Zip' ) Out[5 ]: KeyError: "None of ['ZipCode'] are in the columns" In [6 ]: population = population.reset_index() In [6 ]: unemployment = unemployment.reset_index() In [6 ]: pd.merge(population, unemployment, left_on='ZipCode' , right_on='Zip' ) Out[6 ]: | | ZipCode | 2010 Census Population | Zip | Unemployment | Participants | |---|---------|------------------------|------|--------------|--------------| | 0 | 2860 | 45199 | 2860 | 0.11 | 34447 | In [7 ]: pd.merge(population, unemployment, left_on='ZipCode' , right_on='Zip' , how='outer' ) Out[7 ]: | | ZipCode | 2010 Census Population | Zip | Unemployment | Participants | |---|---------|------------------------|---------|--------------|--------------| | 0 | 57538.0 | 322.0 | NaN | NaN | NaN | | 1 | 59916.0 | 130.0 | NaN | NaN | NaN | | 2 | 37660.0 | 40038.0 | NaN | NaN | NaN | | 3 | 2860.0 | 45199.0 | 2860.0 | 0.11 | 34447.0 | | 4 | NaN | NaN | 46167.0 | 0.02 | 4800.0 | | 5 | NaN | NaN | 1097.0 | 0.33 | 42.0 | | 6 | NaN | NaN | 80808.0 | 0.07 | 4310.0 |
我暂且认为更改ZipCode和Zip的这个行为是个bug,并且已经提交给git了。可以看下之后的反馈:https://github.com/pandas-dev/pandas/issues/34017
当然还是有一些办法去解决这个问题,可是使用会concat()方法来进行合并,或者我们可以通过统一两个DataFrame邮编的label来使用on= [‘Zip’]来进行合并,实验表明通过on= [‘Zip’]进行合并不会出现上述问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 In [8 ]: population.rename(columns={'ZipCode' :'Zip' }, inplace=True ) In [8 ]: merge_2 = pd.merge(population, unemployment, on=['Zip' ], how='outer' ) print (merge_2)Out[8 ]: | | Zip | 2010 Census Population | Unemployment | Participants | Participants | |---|-------|------------------------|--------------|--------------|--------------| | 0 | 57538 | 322.0 | NaN | NaN | NaN | | 1 | 59916 | 130.0 | NaN | NaN | NaN | | 2 | 37660 | 40038.0 | NaN | NaN | NaN | | 3 | 2860 | 45199.0 | 0.11 | 34447.0 | 34447.0 | | 4 | 46167 | NaN | 0.02 | 4800.0 | 4800.0 | | 5 | 1097 | NaN | 0.33 | 42.0 | 42.0 | | 6 | 80808 | NaN | 0.07 | 4310.0 | 4310.0 |
join() Vs. concat()
join有四种合并方法,分别是how='left
‘, how='right'
, how='inner'
和how='outer'
。当然这些合并方法merge()
也是全部都有的。所以看到这里也应该对append()
, concat()
, join()
和merge()
有很充分的理解了。merge()
是四个函数里面最强大的,但是在使用原则上来讲并不是每次对数据操作都要用merge()
,有时候append()
和concat()
使用起来可能会更加方便,在最后会总结一下这四个函数的分类和原则。这里先看一下join()
的实际操作:
1 2 3 4 5 6 7 8 9 In [1 ]: population.join(unemployment) Out[1 ]: | | 2010 Census Population | unemployment | participants | |---------|------------------------|--------------|--------------| | ZipCode | | | | | 57538 | 322 | NaN | NaN | | 59916 | 130 | NaN | NaN | | 37660 | 40038 | NaN | NaN | | 2860 | 45199 | 0.11 | 34447.0 |
df1.join(df2, how=’left’)的意思是指以左边的DataFrame为准进行合并,population在unemployment左边,所以这个合并就会以population的index也就是ZipCode为准进行合并。所以df1.join(df2, how=’right’)就会以unemployment的index进行合并:
1 2 3 4 5 6 7 8 9 In [2 ]: population.join(unemployment, how= 'right' ) Out[2 ]: | | 2010 Census Population | unemployment | participants | |-------|------------------------|--------------|--------------| | Zip | | | | | 2860 | 45199.0 | 0.11 | 34447 | | 46167 | NaN | 0.02 | 4800 | | 1097 | NaN | 0.33 | 42 | | 80808 | NaN | 0.07 | 4310 |
join和concat都是要以index来进行合并,所以在合并时,必须要有对应的index。concat相比join缺少了left和right两种合并方式,但是在outer和inner合并方式来讲得到的结果是一模一样的:
1 2 3 4 5 6 population.join(unemployment, how='outer' ) pd.concat([population, unemployment], join='outer' , axis=1 ) population.join(unemployment, how='inner' ) pd.concat([population, unemployment], join='inner' , axis=1 )
append(), concat(), join()和merge()总结
append()
语法:df1.append(df2)
说明:append()
就是简单的把两个DataFrame纵向罗列起来,不需要index 。
concat()
语法:pd.concat([df1, df2])
说明:concat()
可以横纵向合并多行或者多列,可以使用inner或者outer方式来合并,需要index 。
join()
语法:df1.join(df2)
说明:join()
可以使用多种合并方式,除了inner和outer之外还可以用left和right,这些操作同样需要index 。
merge()
语法:pd.merge([df1, df2])
说明:方式最多的合并函数。不需要index。
merge_order()函数
merge_order()
函数可以用一个函数进行两个操作,即merge()
和sort_value()
。
1 pd.merge_ordered(hardware, software, on=['' , '' ], suffixes=['' , '' ],fill_method='ffill' )
来自于https://mingju.net/2020/05/merging-dataframes-with-pandas/