这一节介绍pandas中的数据结构。首先,导入numpy和pandas:
In [1]:
import numpy as np
import pandas as pd
我们先对数据结构进行简短的介绍, 然后再详细说明各个数据结构内置的方法。
Series是一个一维带label的数组,元素可以是任何数据类型(整数、字符串、浮点数,Python对象等等)。和Python列表一样,Series元素的数据类型可以不同。Series是值可变的数据结构,你可以对它的值进行修改,但是Series的大小是不可以改变的。
Series的label就是index。
创建一个Series对象:
s = pd.Series(data, index=index)
这里,data可以是:
传入的index是label组成的列表。 因此,依据data的不同,我们可以分成以下几种情形:
ndarray
如果data是一个ndarray,index的长度必须和data一样。若没有指明index,则默认创建一个 [0, ..., len(data) - 1]的index。
In [2]:
s = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'])
s
Out[2]:
In [3]:
s.index
Out[3]:
In [4]:
pd.Series(np.random.randn(5))
Out[4]:
注意: 从0.8.0版本开始,index的值可以重复。
In [6]:
pd.Series(np.random.randn(3), index=['a', 'b', 'a'])
Out[6]:
dict字典
如果dict是字典,index的长度可以和data长度不同,也可以不提供index:
In [9]:
d = {'a' : 0., 'b' : 1., 'c' : 2.}
pd.Series(d)
Out[9]:
In [10]:
pd.Series(d, index=['b', 'c', 'd', 'a'])
Out[10]:
注意: NaN(not a number)是pandas中缺失值标记。
标量数值
如果data是一个标量值,必须提供index。为了匹配index的长度,该数值将被重复。
In [11]:
pd.Series(5., index=['a', 'b', 'c', 'd', 'e'])
Out[11]:
大多数情况下,你都可以把Series看做一个ndarray来处理。甚至,大多数的NumPy函数都支持Series作参数。
In [17]:
s[0]
Out[17]:
In [18]:
s[:3]
Out[18]:
In [19]:
s[s > s.median()]
Out[19]:
In [20]:
s[[4,3,1]]
Out[20]:
In [21]:
np.exp(s)
Out[21]:
你可以把Series看做一个长度固定的字典,你可以通过index来获取和设置数值:
In [22]:
s['a']
Out[22]:
In [23]:
s['e'] = 12.
In [24]:
s
Out[24]:
In [25]:
'e' in s
Out[25]:
In [26]:
'f' in s
Out[26]:
如果label值不存在,将抛出异常:
In [27]:
s['f']
使用get方法,遇到缺失的label将返回None或指定值:
In [28]:
s.get('f')
In [29]:
s.get('f', np.nan)
Out[29]:
进行数据分析时,你可以把Series看做向量进行整体操作。大多数接受ndarray作参数的NumPy方法也支持传入Series对象。
In [30]:
s + s
Out[30]:
In [31]:
s * 2
Out[31]:
In [32]:
np.exp(s)
Out[32]:
Series和ndarray之间的主要区别是,Series之间的操作会基于label对数据自动对齐。
所以,你不需要考虑两个参与计算的Series是否有完全相同的label。
In [34]:
s[1:] + s[:-1]
Out[34]:
如果两个label不一样的Series参与计算,存储结果的Series的index/label是它们俩的union(并集)。正是自带label对齐,才使得pandas区别于大多数数据分析库,优越性一览无余。
Series构造函数还有另一个参数:name,
In [35]:
s = pd.Series(np.random.randn(5), name='something')
In [32]:
s
Out[32]:
In [36]:
s.name
Out[36]:
通过pandas.Series.rename()方法,可以给Series重命名。
In [37]:
s2 = s.rename("different")
s2.name
Out[37]:
In [38]:
s.name
Out[38]:
注意s和s2是两个不同对象。
DataFrame是一个带label的二维数据结构。你可以把它看做一张SQL表,或是由Series构成的字典。它基本上是最常用的pandas数据结构。
和Series一样,DataFrame构造函数支持许多不同类型的输入:
DataFrame构造函数也支持传入index(行label)和columns(列label)参数。
结果的index是各个Series的index的union。如果存在任何嵌套类型的字典,将首先转换成Series。如果不传入columns,则默认把字典的键构成的有序列表看做columns。
In [39]:
d = {'one' : pd.Series([1., 2., 3.], index=['a', 'b', 'c']),
'two' : pd.Series([1., 2., 3., 4.], index=['a', 'b', 'c', 'd'])}
In [40]:
df = pd.DataFrame(d)
In [41]:
df
Out[41]:
In [42]:
pd.DataFrame(d, index=['d','b','a'])
Out[42]:
In [44]:
pd.DataFrame(d, index=['d', 'b', 'a'], columns=['two', 'three'])
Out[44]:
行label和列label可分别通过访问index和columns获取:
In [45]:
df.index
Out[45]:
In [46]:
df.columns
Out[46]:
ndarrays必须具有相同的长度。如果传入index参数,它的长度也必须和ndarray长度相同。如果不传入index参数,DataFrame会默认创建index=range(n),其中n是ndarray的长度。
In [48]:
d = {'one' : [1., 2., 3., 4.],
'two' : [4., 3., 2., 1.]}
In [49]:
pd.DataFrame(d)
Out[49]:
In [51]:
pd.DataFrame(d, index=['a', 'b', 'c', 'd'])
Out[51]:
In [52]:
data = np.zeros((2,), dtype=[('A', 'i4'),('B', 'f4'),('C', 'a10')])
data[:] = [(1,2.,'Hello'), (2,3.,"World")]
pd.DataFrame(data)
Out[52]:
In [53]:
pd.DataFrame(data, index=['first','second'])
Out[53]:
In [54]:
pd.DataFrame(data, columns=['C','A','B'])
Out[54]:
In [55]:
data2 = [{'a': 1, 'b': 2}, {'a': 5, 'b': 10, 'c': 20}]
In [56]:
pd.DataFrame(data2)
Out[56]:
In [57]:
pd.DataFrame(data2, index=['first','second'])
Out[57]:
In [58]:
pd.DataFrame(data2, columns=['a','b'])
Out[58]:
In [59]:
pd.DataFrame({('a', 'b'): {('A', 'B'): 1, ('A', 'C'): 2},
('a', 'a'): {('A', 'C'): 3, ('A', 'B'): 4},
('a', 'c'): {('A', 'B'): 5, ('A', 'C'): 6},
('b', 'a'): {('A', 'C'): 7, ('A', 'B'): 8},
('b', 'b'): {('A', 'D'): 9, ('A', 'B'): 10}})
Out[59]:
结果是创建一个列数为1的DataFrame,它的index和输入的Series相同,column名是Series的name。
DataFrame.from_dict
DataFrame.from_dict接受字典的字典或数组序列的字典,并返回一个DataFrame。
DataFrame.from_records
DataFrame.from_records接受一个元组列表或结构化类型的ndarray。例如:
In [60]:
data
Out[60]:
In [61]:
pd.DataFrame.from_records(data, index='C')
Out[61]:
DataFrame.from_items
In [62]:
pd.DataFrame.from_items([('A', [1, 2, 3]), ('B', [4, 5, 6])])
Out[62]:
In [63]:
pd.DataFrame.from_items([('A', [1, 2, 3]), ('B', [4, 5, 6])],
orient='index', columns=['one', 'two', 'three'])
Out[63]:
你可以把DataFrame看做Series构成的字典,使用和字典同样的操作来增加列、删除列、对列重新赋值:
In [64]:
df['one']
Out[64]:
In [65]:
df['three'] = df['one'] * df['two']
In [66]:
df['flag'] = df['one'] > 2
In [67]:
df
Out[67]:
列可以删除或像字典一样被弹出
In [68]:
del df['two']
In [69]:
three = df.pop('three')
In [70]:
df
Out[70]:
当插入一个标量值,它自然会被填充到列中
In [71]:
df['foo'] = 'bar'
In [72]:
df
Out[72]:
当插入不具有相同的index的Series时,将服从该DataFrame的索引:
In [73]:
df['one_trunc'] = df['one'][:2]
In [74]:
df
Out[74]:
你可以插入ndarray,但其长度必须与DataFrame的index长度相匹配。
缺省情况下,列被插在末端。insert()函数可以允许插入到指定的列位置:
In [75]:
df.insert(1,'bar',df['one'])
In [76]:
df
Out[76]:
DataFrame有一个assign()方法,利用现有列来创建新列。
iris = pd.read_csv('data/iris.data')
iris.head()
(iris.assign(sepal_ratio = iris['SepalWidth'] / iris['SepalLength']) .head())
iris.assign(sepal_ratio = lambda x: (x['SepalWidth'] / x['SepalLength'])).head()
(iris.query('SepalLength > 5') .assign(SepalRatio = lambda x: x.SepalWidth / x.SepalLength, PetalRatio = lambda x: x.PetalWidth / x.PetalLength) .plot(kind='scatter', x='SepalRatio', y='PetalRatio'))
检索的基础知识如下:
操作 | 语法 | 结果 |
---|---|---|
列选择 | df[col] | Series |
通过label来选择行 | df.loc[label] | Series |
通过下标选择行 | df.iloc[loc] | Series |
选择部分行 | df[5:10] | DataFrame |
通过布尔向量选择行 | df[bool_vec] | DataFrame |
行选择,例如,返回一个index为DataFrame columns的Series:
In [78]:
df
Out[78]:
In [79]:
df.loc['b']
Out[79]:
In [80]:
df.iloc[2]
Out[80]:
DataFrame对象之间会自动对index和column进行数据对齐。运算结果的index和columns分别是参与运算的DataFrame的index和columns的并集(union)。
In [81]:
df = pd.DataFrame(np.random.randn(10,4),columns=['A', 'B', 'C', 'D'])
In [82]:
df2 = pd.DataFrame(np.random.randn(7,3),columns=['A', 'B', 'C'])
In [83]:
df + df2
Out[83]:
DataFrame与Series进行运算时, 默认行为是DataFrame的column和Series的index对齐,也就是对Series进行行广播。例如:
In [85]:
df - df.iloc[0]
Out[85]:
在特殊情况下, 处理时间序列数据时, DataFrame的index包含日期,广播会变成列广播:
In [94]:
index = pd.date_range('1/1/2000',periods=8)
In [95]:
df = pd.DataFrame(np.random.randn(8,3), index=index, columns=list('ABC'))
In [96]:
df
Out[96]:
In [97]:
type(df['A'])
Out[97]:
In [98]:
df - df['A']
Out[98]:
In [104]:
df.sub(df['A'], axis=0) #
Out[104]:
In [100]:
df * 5 + 2
Out[100]:
In [101]:
1 / df
Out[101]:
In [102]:
df ** 4
Out[102]:
布尔运算
In [105]:
df1 = pd.DataFrame({'a' : [1, 0, 1], 'b' : [0, 1, 1] }, dtype=bool)
In [106]:
df2 = pd.DataFrame({'a' : [0, 1, 1], 'b' : [1, 1, 0] }, dtype=bool)
In [107]:
df1 & df2
Out[107]:
In [108]:
df1 | df2
Out[108]:
In [109]:
df1 ^ df2
Out[109]:
In [110]:
-df1
Out[110]:
转置, T属性(转置函数):
In [111]:
df[:5].T
Out[111]:
In [112]:
df
Out[112]:
In [113]:
np.exp(df)
Out[113]:
In [114]:
np.asarray(df)
Out[114]:
dot方法能够实现DataFrame矩阵乘法:
In [115]:
df.T.dot(df)
Out[115]:
类似地,dot方法也能运用在Series上:
In [116]:
s1 = pd.Series(np.arange(5,10))
In [117]:
s1.dot(s1)
Out[117]:
在控制台,非常大的DataFrames将被截断显示它们。你也可以使用info()查看详情。
baseball = pd.read_csv('data/baseball.csv')
print(baseball)
baseball.info()
然而,使用to_string,DataFrame将返回一个字符串表示的表格形式,虽然它并不总是适合控制台宽度:
print(baseball.iloc[-20:, :12].to_string())
pd.DataFrame(np.random.randn(3, 12))
你可以设置display.width值,来改变单行打印的数量。
pd.set_option('display.width', 40)
pd.DataFrame(np.random.randn(3, 12))
如果DataFrame的列名是一个有效的Python变量名, 就可以像访问属性一样访问列数据:
In [118]:
df = pd.DataFrame({'foo1':np.random.randn(5),'foo2':np.random.randn(5)})
In [119]:
df
Out[119]:
In [121]:
df.foo1
Out[121]:
Panel比较少用,但是对于三维数据很有用。
In [ ]:
In [ ]:
In [ ]:
In [ ]:
In [ ]: