In [1]:
import numpy as np
import pandas as pd
Python和Numpy的索引操作符[]和属性操作符‘.’能够快速检索pandas数据。
然而,这两种方式的效率在pandas中可能不是最优的,我们推荐使用专门优化过的pandas数据检索方法。而这些方法则是本节要介绍的。
pandas支持三种不同的索引方式:
以 .loc为例看一下使用方式:
对象类型 | Indexers
Series | s.loc[indexer]
DataFrame | df.loc[row_indexer, column_indexer]
Panel | p.loc[item_indexer, major_indexer, minor_indexer]
最基本的选择数据方式就是使用[]操作符进行索引,
对象类型 | Selection | 返回值类型
Series | series[label],这里的label是index名 | 常数
DataFrame| frame[colname],使用列名 | Series对象,相应的colname那一列
Panel | panel[itemname] | DataFrame对象,相应的itemname那一个
下面用示例展示一下
In [62]:
dates = pd.date_range('1/1/2000', periods=8)
dates
Out[62]:
In [63]:
df = pd.DataFrame(np.random.randn(8,4), index=dates, columns=list('ABCD'))
df
Out[63]:
In [64]:
panel = pd.Panel({'one':df, 'two':df-df.mean()})
panel
Out[64]:
In [ ]:
我们使用最基本的[]操作符
In [5]:
s = df['A'] #使用列名
s#返回的是 Series
Out[5]:
Series使用index索引
In [6]:
s[dates[5]] #使用index名
Out[6]:
In [7]:
panel['two']
Out[7]:
也可以给[]传递一个column name组成的的list,形如df[[col1,col2]], 如果给出的某个列名不存在,会报错
In [8]:
df
Out[8]:
In [9]:
df[['B', 'A']] = df[['A', 'B']]
df
Out[9]:
In [ ]:
In [ ]:
可以直接把Series的index、DataFrame中的column、Panel中的item作为这些对象的属性使用,然后直接访问相应的index、column、item
In [10]:
sa = pd.Series([1,2,3],index=list('abc'))
dfa = df.copy()
In [11]:
sa
Out[11]:
In [12]:
sa.b #直接把index作为属性
Out[12]:
In [13]:
dfa
Out[13]:
In [14]:
dfa.A
Out[14]:
In [15]:
panel.one
Out[15]:
In [ ]:
In [ ]:
In [16]:
sa
Out[16]:
In [17]:
sa.a = 5
sa
Out[17]:
In [18]:
sa
Out[18]:
In [19]:
dfa.A=list(range(len(dfa.index))) # ok if A already exists
In [20]:
dfa
Out[20]:
In [21]:
dfa['A'] = list(range(len(dfa.index))) # use this form to create a new column
dfa
Out[21]:
In [ ]:
In [ ]:
注意:使用属性和[] 有一点区别:
如果要新建一个column,只能使用[]
毕竟属性的含义就是现在存在的!不存在的列名当然不是属性了
You can use attribute access to modify an existing element of a Series or column of a DataFrame, but be careful; if you try to use attribute access to create a new column, it fails silently, creating a new attribute rather than a new column.
使用属性要注意的:
In [ ]:
可以使用 [] 还有.iloc切片,这里先介绍使用[]
In [ ]:
对于Series来说,使用[]进行切片就像ndarray一样,
In [22]:
s
Out[22]:
In [23]:
s[:5]
Out[23]:
In [24]:
s[::2]
Out[24]:
In [ ]:
In [25]:
s[::-1]
Out[25]:
[]不但可以检索,也可以赋值
In [65]:
s2 = s.copy()
In [66]:
s2[:5]=0 #赋值
In [67]:
s2
Out[67]:
对于DataFrame对象来说,[]操作符按照行进行切片,非常有用。
In [68]:
df[:3]
Out[68]:
In [69]:
df[::-1]
Out[69]:
In [ ]:
警告:
.loc要求检索时输入必须严格遵守index的类型,一旦输入类型不对,将会引起TypeError。
In [70]:
df1 = pd.DataFrame(np.random.rand(5,4), columns=list('ABCD'), index=pd.date_range('20160101',periods=5))
df1
Out[70]:
In [71]:
df1.loc[2:3]
输入string进行检索没问题
In [72]:
df1.loc['20160102':'20160104']
Out[72]:
细心地你一定发现了,index='20160104'那一行也被检索出来了,没错,loc检索时范围是闭集合[start,end].
整型可以作为label检索,这是没问题的,不过要记住此时整型表示的是label而不是index中的下标!
.loc操作是检索时的基本操作,以下输入格式都是合法的:
In [ ]:
In [73]:
s1 = pd.Series(np.random.randn(6), index=list('abcdef'))
s1
Out[73]:
In [74]:
s1.loc['c':]
Out[74]:
In [75]:
s1.loc['b']
Out[75]:
In [ ]:
loc同样支持赋值操作
In [76]:
s1.loc['c':]=0
s1
Out[76]:
In [ ]:
再来看看DataFramed的例子
In [77]:
df1 = pd.DataFrame(np.random.randn(6,4), index=list('abcdef'),columns=list('ABCD'))
df1
Out[77]:
In [78]:
df1.loc[['a','b','c','d'],:]
Out[78]:
In [79]:
df1.loc[['a','b','c','d']] #可以省略 ':'
Out[79]:
In [ ]:
使用切片检索
In [80]:
df1.loc['d':,'A':'C'] #注意是闭集合
Out[80]:
In [81]:
df1.loc['a']
Out[81]:
使用布尔数组检索
In [82]:
df1.loc['a']>0
Out[82]:
In [83]:
df1.loc[:,df1.loc['a']>0]
Out[83]:
得到DataFrame中的某一个值, 等同于df1.get_value('a','A')
In [84]:
df1.loc['a','A']
Out[84]:
In [85]:
df1.get_value('a','A')
Out[85]:
pandas提供了一系列的方法实现基于整型的检索。语义和python、numpy切片几乎一样。下标同样都是从0开始,并且进行的是半闭半开的区间检索[start,end)。如果输入 非整型label当做下标进行检索会引起IndexError。
.iloc的合法输入包括:
看一下Series使用iloc检索的示例:
In [86]:
s1 = pd.Series(np.random.randn(5),index=list(range(0,10,2)))
s1
Out[86]:
In [87]:
s1.iloc[:3] #注意检索是半闭半开区间
Out[87]:
In [88]:
s1.iloc[3]
Out[88]:
iloc同样也可以进行赋值
In [89]:
s1.iloc[:3]=0
s1
Out[89]:
In [ ]:
DataFrame的示例:
In [90]:
df1 = pd.DataFrame(np.random.randn(6,4),index=list(range(0,12,2)), columns=list(range(0,8,2)))
df1
Out[90]:
In [91]:
df1.iloc[:3]
Out[91]:
进行行和列的检索
In [92]:
df1.iloc[1:5,2:4]
Out[92]:
In [93]:
df1.iloc[[1,3,5],[1,2]]
Out[93]:
In [94]:
df1.iloc[1:3,:]
Out[94]:
In [95]:
df1.iloc[:,1:3]
Out[95]:
In [96]:
df1.iloc[1,1]#只检索一个元素
Out[96]:
注意下面两个例子的区别:
In [97]:
df1.iloc[1]
Out[97]:
In [98]:
df1.iloc[1:2]
Out[98]:
In [ ]:
如果切片检索时输入的范围越界,没关系,只要pandas版本>=v0.14.0, 就能如同Python/Numpy那样正确处理。
注意:仅限于 切片检索
In [99]:
x = list('abcdef')
x
Out[99]:
In [100]:
x[4:10] #这里x的长度是6
Out[100]:
In [101]:
x[8:10]
Out[101]:
In [ ]:
In [102]:
s = pd.Series(x)
In [103]:
s
Out[103]:
In [104]:
s.iloc[4:10]
Out[104]:
In [105]:
s.iloc[8:10]
Out[105]:
In [ ]:
In [106]:
df1 = pd.DataFrame(np.random.randn(5,2), columns=list('AB'))
df1
Out[106]:
In [107]:
df1.iloc[:,2:3]
Out[107]:
In [108]:
df1.iloc[:,1:3]
Out[108]:
In [109]:
df1.iloc[4:6]
Out[109]:
上面说到,这种优雅处理越界的能力仅限于输入全是切片,如果输入是越界的 列表或者整数,则会引起IndexError
In [110]:
df1.iloc[[4,5,6]]
输入有切片,有整数,如果越界同样不能处理
In [111]:
df1.iloc[:,4]
使用sample()方法能够从行或者列中进行随机选择,适用对象包括Series、DataFrame和Panel。sample()方法默认对行进行随机选择,输入可以是整数或者小数。
In [112]:
s = pd.Series([0,1,2,3,4,5])
s
Out[112]:
In [113]:
s.sample()
Out[113]:
In [114]:
s.sample(n=6)
Out[114]:
In [115]:
s.sample(3) #直接输入整数即可
Out[115]:
也可以输入小数,则会随机选择N*frac个样本, 结果进行四舍五入
In [116]:
s.sample(frac=0.5)
Out[116]:
In [117]:
s.sample(0.5) #必须输入frac=0.5
In [118]:
s.sample(frac=0.8) #6*0.8=4.8
Out[118]:
In [119]:
s.sample(frac=0.7)# 6*0.7=4.2
Out[119]:
sample()默认进行的无放回抽样,可以利用replace=True参数进行可放回抽样。
In [120]:
s
Out[120]:
In [121]:
s.sample(n=6,replace=False)
Out[121]:
In [122]:
s.sample(6,replace=True)
Out[122]:
默认情况下,每一行/列都被等可能的采样,如果你想为每一行赋予一个被抽样选择的权重,可以利用weights参数实现。
注意:如果weights中各概率相加和不等于1,pandas会先对weights进行归一化,强制转为概率和为1!
In [123]:
s = pd.Series([0,1,2,3,4,5])
s
Out[123]:
In [124]:
example_weights=[0,0,0.2,0.2,0.2,0.4]
In [125]:
s.sample(n=3,weights=example_weights)
Out[125]:
In [126]:
example_weights2 = [0.5, 0, 0, 0, 0, 0]
In [127]:
s.sample(n=1, weights=example_weights2)
Out[127]:
In [128]:
s.sample(n=2, weights=example_weights2) #n>1 会报错,
注意:由于sample默认进行的是无放回抽样,所以输入必须n<=行数,除非进行可放回抽样。
In [129]:
s
Out[129]:
In [130]:
s.sample(7) #7不行
In [131]:
s.sample(7,replace=True)
Out[131]:
如果是对DataFrame对象进行有权重采样,一个简单 的方法是新增一列用于表示每一行的权重
In [134]:
df2 = pd.DataFrame({'col1':[9,8,7,6], 'weight_column':[0.5, 0.4, 0.1, 0]})
df2
Out[134]:
In [135]:
df2.sample(n=3,weights='weight_column')
Out[135]:
对列进行采样, axis=1
In [136]:
df3 = pd.DataFrame({'col1':[1,2,3], 'clo2':[2,3,4]})
df3
Out[136]:
In [137]:
df3.sample(1,axis=1)
Out[137]:
我们也可以使用random_state参数 为sample内部的随机数生成器提供种子数。
In [138]:
df4 = pd.DataFrame({'col1':[1,2,3], 'clo2':[2,3,4]})
df4
Out[138]:
注意下面两个示例,输出是相同的,因为使用了相同的种子数
In [139]:
df4.sample(n=2, random_state=2)
Out[139]:
In [140]:
df4.sample(n=2,random_state=2)
Out[140]:
In [141]:
df4.sample(n=2,random_state=3)
Out[141]:
In [ ]:
用.loc/.ix/[]对不存在的键值进行赋值时,将会导致在对象中添加新的元素,它的键即为赋值时不存在的键。
对于Series来说,这是一种有效的添加操作。
In [143]:
se = pd.Series([1,2,3])
se
Out[143]:
In [144]:
se[5]=5
se
Out[144]:
DataFrame可以在行或者列上扩充数据
In [145]:
dfi = pd.DataFrame(np.arange(6).reshape(3,2),columns=['A','B'])
dfi
Out[145]:
In [148]:
dfi.loc[:,'C']=dfi.loc[:,'A'] #对列进行扩充
In [149]:
dfi
Out[149]:
In [152]:
dfi.loc[3]=5 #对行进行扩充
In [153]:
dfi
Out[153]:
如果仅仅想获取一个元素,使用[]未免太繁重了。pandas提供了快速获取一个元素的方法:at和iat. 适用于Series、DataFrame和Panel。
如果loc方法,at方法的合法输入是label,iat的合法输入是整型。
In [154]:
s.iat[5]
Out[154]:
In [155]:
df.at[dates[5],'A']
Out[155]:
In [156]:
df.iat[3,0]
Out[156]:
也可以进行赋值操作
In [157]:
df.at[dates[-1]+1,0]=7
df
Out[157]:
另一种常用的操作是使用布尔向量过滤数据。运算符有三个:|(or), &(and), ~(not)。
注意:运算符的操作数要在圆括号内。
使用布尔向量检索Series的操作方式和numpy ndarray一样。
In [159]:
s = pd.Series(range(-3, 4))
s
Out[159]:
In [160]:
s[s>0]
Out[160]:
In [161]:
s[(s<-1) | (s>0.5)]
Out[161]:
In [162]:
s[~(s<0)]
Out[162]:
DataFrame示例:
In [165]:
df[df['A'] > 0]
Out[165]:
利用列表解析和map方法能够产生更加复杂的选择标准。
In [169]:
df2 = pd.DataFrame({'a' : ['one', 'one', 'two', 'three', 'two', 'one', 'six'],
'b' : ['x', 'y', 'y', 'x', 'y', 'x', 'x'],
'c' : np.random.randn(7)})
df2
Out[169]:
In [170]:
criterion = df2['a'].map(lambda x:x.startswith('t'))
In [171]:
df2[criterion]
Out[171]:
In [172]:
df2[[x.startswith('t') for x in df2['a']]]
Out[172]:
In [173]:
df2[criterion & (df2['b'] == 'x')]
Out[173]:
结合loc、iloc等方法可以检索多个坐标下的数据.
In [174]:
df2.loc[criterion & (df2['b'] == 'x'), 'b':'c']
Out[174]:
isin(is in)
对于Series对象来说,使用isin方法时传入一个列表,isin方法会返回一个布尔向量。布尔向量元素为1的前提是列表元素在Series对象中存在。看起来比较拗口,还是看例子吧:
In [175]:
s = pd.Series(np.arange(5), index=np.arange(5)[::-1],dtype='int64')
In [176]:
s
Out[176]:
In [177]:
s.isin([2,4,6])
Out[177]:
In [178]:
s[s.isin([2,4,6])]
Out[178]:
Index对象中也有isin方法.
In [179]:
s[s.index.isin([2,4,6])]
Out[179]:
In [180]:
s[[2,4,6]]
Out[180]:
In [ ]:
DataFrame同样有isin方法,参数是数组或字典。二者的区别看例子吧:
In [182]:
df = pd.DataFrame({'vals': [1, 2, 3, 4], 'ids': ['a', 'b', 'f', 'n'],
'ids2':['a', 'n', 'c', 'n']})
df
Out[182]:
In [183]:
values=['a', 'b', 1, 3]
In [184]:
df.isin(values)
Out[184]:
输入一个字典的情形:
In [185]:
values = {'ids': ['a', 'b'], 'vals': [1, 3]}
In [186]:
df.isin(values)
Out[186]:
结合isin方法和any() all()可以对DataFrame进行快速查询。比如选择每一列都符合标准的行:
In [187]:
values = {'ids': ['a', 'b'], 'ids2': ['a', 'c'], 'vals': [1, 3]}
In [188]:
row_mark = df.isin(values).all(1)
In [189]:
df[row_mark]
Out[189]:
In [198]:
row_mark = df.isin(values).any(1)
In [199]:
df[row_mark]
Out[199]:
In [ ]:
使用布尔向量对Series对象查询时通常返回的是对象的子集。如果想要返回的shape和原对象相同,可以使用where方法。
使用布尔向量对DataFrame对象查询返回的shape和原对象相同,这是因为底层用的where方法实现。
In [205]:
s[s>0]
Out[205]:
使用where方法
In [206]:
s.where(s>0)
Out[206]:
In [207]:
df[df<0]
Out[207]:
In [208]:
df.where(df<0)
Out[208]:
where方法还有一个可选的other参数,作用是替换返回结果中是False的值,并不会改变原对象。
In [214]:
df.where(df<0, 2)
Out[214]:
In [211]:
df
Out[211]:
In [215]:
df.where(df<0, df) #将df作为other的参数值
Out[215]:
你可能想基于某种判断条件来赋值。一种直观的方法是:
In [216]:
s2 = s.copy()
s2
Out[216]:
In [217]:
s2[s2<0]=0
In [218]:
s2
Out[218]:
默认情况下,where方法并不会修改原始对象,它返回的是一个修改过的原始对象副本,如果你想直接修改原始对象,方法是将inplace参数设置为True
In [223]:
df = pd.DataFrame(np.random.randn(6,5), index=list('abcdef'), columns=list('ABCDE'))
df_orig = df.copy()
In [226]:
df_orig.where(df < 0, -df, inplace=True);
In [227]:
df_orig
Out[227]:
对齐
where方法会将输入的布尔条件对齐,因此允许部分检索时的赋值。
In [231]:
df2 = df.copy()
In [232]:
df2[df2[1:4] >0]=3
In [233]:
df2
Out[233]:
In [234]:
df2 = df.copy()
In [235]:
df2.where(df2>0, df2['A'], axis='index')
Out[235]:
mask
In [236]:
s.mask(s>=0)
Out[236]:
In [237]:
df.mask(df >= 0)
Out[237]:
DataFrame对象拥有query方法,允许使用表达式检索。
比如,检索列'b'的值介于列‘a’和‘c’之间的行。
注意: 需要安装numexptr。
In [7]:
n = 10
In [8]:
df = pd.DataFrame(np.random.randn(n, 3), columns=list('abc'))
In [9]:
df
Out[9]:
In [10]:
df[(df.a<df.b) & (df.b<df.c)]
Out[10]:
In [11]:
df.query('(a < b) & (b < c)') #
Out[11]:
对于DataFrame对象,可以使用MultiIndex,如同操作列名一样。
In [14]:
n = 10
In [15]:
colors = np.random.choice(['red', 'green'], size=n)
In [16]:
foods = np.random.choice(['eggs', 'ham'], size=n)
In [17]:
colors
Out[17]:
In [18]:
foods
Out[18]:
In [19]:
index = pd.MultiIndex.from_arrays([colors, foods], names=['color', 'food'])
In [20]:
df = pd.DataFrame(np.random.randn(n,2), index=index)
In [21]:
df
Out[21]:
In [22]:
df.query('color == "red"')
Out[22]:
如果index没有名字,可以给他们命名
In [23]:
df.index.names = [None, None]
In [24]:
df
Out[24]:
In [25]:
df.query('ilevel_0 == "red"')
Out[25]:
ilevl_0意思是 0级index。
一个使用query()的情景是面对DataFrame对象组成的集合,并且这些对象有共同的的列名,则可以利用query方法对这个集合进行统一检索。
In [26]:
df = pd.DataFrame(np.random.randn(n, 3), columns=list('abc'))
df
Out[26]:
In [28]:
df2 = pd.DataFrame(np.random.randn(n+2, 3), columns=df.columns)
df2
Out[28]:
In [29]:
expr = '0.0 <= a <= c <= 0.5'
In [30]:
map(lambda frame: frame.query(expr), [df, df2])
Out[30]:
In [31]:
df = pd.DataFrame(np.random.randint(n, size=(n, 3)), columns=list('abc'))
df
Out[31]:
In [32]:
df.query('(a<b) &(b<c)')
Out[32]:
In [33]:
df[(df.a < df.b) & (df.b < df.c)]
Out[33]:
query()可以去掉圆括号, 也可以用and 代替&运算符
In [35]:
df.query('a < b & b < c')
Out[35]:
In [36]:
df.query('a<b and b<c')
Out[36]:
query()也支持Python中的in和not in运算符,实际上是底层调用isin
In [37]:
df = pd.DataFrame({'a': list('aabbccddeeff'), 'b': list('aaaabbbbcccc'),
'c': np.random.randint(5, size=12),
'd': np.random.randint(9, size=12)})
In [38]:
df
Out[38]:
In [39]:
df.query('a in b')
Out[39]:
In [40]:
df[df.a.isin(df.b)]
Out[40]:
In [41]:
df[~df.a.isin(df.b)]
Out[41]:
In [42]:
df.query('a in b and c < d') #更复杂的例子
Out[42]:
In [43]:
df[df.b.isin(df.a) & (df.c < df.d)] #Python语法
Out[43]:
可以使用==/!=将列表和列名直接进行比较,等价于使用in/not in.
三种方法功能等价: ==/!= VS in/not in VS isin()/~isin()
In [49]:
df.query('b==["a", "b", "c"]')
Out[49]:
In [50]:
df[df.b.isin(["a", "b", "c"])] #Python语法
Out[50]:
In [51]:
df.query('c == [1, 2]')
Out[51]:
In [52]:
df.query('c != [1, 2]')
Out[52]:
In [53]:
df.query('[1, 2] in c') #使用in
Out[53]:
In [54]:
df.query('[1, 2] not in c')
Out[54]:
In [55]:
df[df.c.isin([1, 2])] #Python语法
Out[55]:
可以使用not或者~对布尔表达式进行取非。
In [56]:
df = pd.DataFrame(np.random.randn(n, 3), columns=list('abc'))
df
Out[56]:
In [57]:
df['bools']=np.random.randn(len(df))>0.5
In [58]:
df
Out[58]:
In [59]:
df.query('bools')
Out[59]:
In [60]:
df.query('not bools')
Out[60]:
In [61]:
df.query('not bools') == df[~df.bools]
Out[61]:
表达式任意复杂都没关系。
In [62]:
shorter = df.query('a<b<c and (not bools) or bools>2')
shorter
Out[62]:
In [63]:
longer = df[(df.a < df.b) & (df.b < df.c) & (~df.bools) | (df.bools > 2)]
longer
Out[63]:
In [64]:
shorter == longer
Out[64]:
DataFrame.query()底层使用numexptr,所以速度要比Python快,特别时当DataFrame对象非常大时。
In [ ]:
如果你想确定和去掉DataFrame对象中重复的行,pandas提供了两个方法:duplicated和drop_duplicates. 两个方法的参数都是列名。
默认情况下,首次遇到的行被认为是唯一的,以后遇到内容相同的行都被认为是重复的。不过两个方法都有一个keep参数来确定目标行是否被保留。
In [66]:
df2 = pd.DataFrame({'a': ['one', 'one', 'two', 'two', 'two', 'three', 'four'],
'b': ['x', 'y', 'x', 'y', 'x', 'x', 'x'],
'c': np.random.randn(7)})
df2
Out[66]:
In [69]:
df2.duplicated('a') #只观察列a的值是否重复
Out[69]:
In [68]:
df2.duplicated('a', keep='last')
Out[68]:
In [70]:
df2.drop_duplicates('a')
Out[70]:
In [71]:
df2.drop_duplicates('a', keep='last')
Out[71]:
In [72]:
df2.drop_duplicates('a', keep=False)
Out[72]:
可以传递列名组成的列表
In [76]:
df2.duplicated(['a', 'b']) #此时列a和b两个元素构成每一个检索的基本单位,
Out[76]:
In [77]:
df2
Out[77]:
也可以检查index值是否重复来去掉重复行,方法是Index.duplicated然后使用切片操作(因为调用Index.duplicated会返回布尔向量)。keep参数同上。
In [89]:
df3 = pd.DataFrame({'a': np.arange(6),
'b': np.random.randn(6)},
index=['a', 'a', 'b', 'c', 'b', 'a'])
In [90]:
df3
Out[90]:
In [91]:
df3.index.duplicated() #布尔表达式
Out[91]:
In [92]:
df3[~df3.index.duplicated()]
Out[92]:
In [93]:
df3[~df3.index.duplicated(keep='last')]
Out[93]:
In [94]:
df3[~df3.index.duplicated(keep=False)]
Out[94]:
Serires, DataFrame和Panel都有一个get方法来得到一个默认值。
In [95]:
s = pd.Series([1,2,3], index=['a', 'b', 'c'])
s
Out[95]:
In [96]:
s.get('a')
Out[96]:
In [97]:
s.get('x', default=-1)
Out[97]:
In [99]:
s.get('b')
Out[99]:
Series, DataFrame和Panel都有select()方法来检索数据,这个方法作为保留手段通常其他方法都不管用的时候才使用。select接受一个函数(在label上进行操作)作为输入返回一个布尔值。
In [101]:
df = pd.DataFrame(np.random.randn(10, 3), columns=list('ABC'))
In [102]:
df.select(lambda x: x=='A', axis=1)
Out[102]:
输入行label和列label,得到一个numpy数组,这就是lookup方法的功能。
In [103]:
dflookup = pd.DataFrame(np.random.randn(20, 4), columns=list('ABCD'))
dflookup
Out[103]:
In [104]:
dflookup.lookup(list(range(0,10,2)), ['B','C','A','B','D'])
Out[104]:
pandas中的Index类和它的子类可以被当做一个序列可重复集合(ordered multiset),允许数据重复。然而,如果你想把一个有重复值Index对象转型为一个集合这是不可以的。创建Index最简单的方法就是通过传递一个列表或者其他序列创建。
In [105]:
index = pd.Index(['e', 'd', 'a', 'b'])
index
Out[105]:
In [106]:
'd' in index
Out[106]:
还可以个Index命名
In [107]:
index = pd.Index(['e', 'd', 'a', 'b'], name='something')
index.name
Out[107]:
In [108]:
index = pd.Index(list(range(5)), name='rows')
In [110]:
columns = pd.Index(['A', 'B', 'C'], name='cols')
In [111]:
df = pd.DataFrame(np.random.randn(5, 3), index=index, columns=columns)
In [112]:
df
Out[112]:
In [113]:
df['A']
Out[113]:
当对pandas对象赋值时,一定要注意避免链式索引(chained indexing)。看下面的例子:
In [114]:
dfmi = pd.DataFrame([list('abcd'),
list('efgh'),
list('ijkl'),
list('mnop')],
columns=pd.MultiIndex.from_product([['one','two'],
['first','second']]))
dfmi
Out[114]:
比较下面两种访问方式:
In [119]:
dfmi['one']['second']
Out[119]:
In [118]:
dfmi.loc[:,('one','second')]
Out[118]:
上面两种方法返回的结果抖一下,那么应该使用哪种方法呢?答案是我们更推荐大家使用方法二。
dfmi['one']选择了第一级列然后返回一个DataFrame对象,然后另一个Python操作dfmi_with_one['second']根据'second'检索出了一个Series。对pandas来说,这两个操作是独立、有序执行的。而.loc方法传入一个元组(slice(None),('one','second')),pandas把这当作一个事件执行,所以执行速度更快。
刚才谈到不推荐使用链式索引是出于性能的考虑。接下来从赋值角度谈一下不推荐使用链式索引。首先,思考Python怎么解释执行下面的代码?
In [ ]:
dfmi.loc[:,('one','second')]=value
#实际是
dfmi.loc.__setitem__((slice(None), ('one', 'second')), value)
但下面的代码解释后结果却不一样:
In [ ]:
dfmi['one']['second'] = value
# becomes
dfmi.__getitem__('one').__setitem__('second', value)
看到getitem了吗?除了最简单的情况,我们很难预测他到底返回的是视图还是副本(哲依赖于数组的内存布局,这是pandas没有硬性要求的),因此不推荐使用链式索引赋值!
而dfmi.loc.setitem直接对dfmi进行操作。
有时候明明没有使用链式索引,也会引起SettingWithCopy警告,这是Pandas设计的bug~
In [123]:
def do_something(df):
foo = df[['bar', 'baz']] # Is foo a view? A copy? Nobody knows!
# ... many lines here ...
foo['quux'] = value # We don't know whether this will modify df or not!
return foo
此外,在链式表达式中,不同的顺序也可能导致不同的结果。这里的顺序指的是检索时行和列的顺序。
In [124]:
dfb = pd.DataFrame({'a' : ['one', 'one', 'two',
'three', 'two', 'one', 'six'],
'c' : np.arange(7)})
dfb
Out[124]:
In [126]:
dfb['c'][dfb.a.str.startswith('o')] = 42 #虽然会引起SettingWithCopyWarning 但也能得到正确结果
In [128]:
pd.set_option('mode.chained_assignment','warn')
dfb[dfb.a.str.startswith('o')]['c'] = 42 #这实际上是对副本赋值!
正确的方式是:老老实实使用.loc
In [133]:
dfc = pd.DataFrame({'A':['aaa','bbb','ccc'],'B':[1,2,3]})
dfc
Out[133]:
In [134]:
dfc.loc[0,'A'] = 11
dfc
Out[134]: