前言
作为Python中进行数据分析最重要的库之一,Pandas可以帮助我们轻松上手数据分析。在这篇文章中,我会详细总结Pandas的一些基本概念和操作语法,同时我也会将Pandas与SQL做一个简单的对比。话不多说,我们开始。首先我们先导入Pandas库以及与其高度相关的基础库Numpy。
import pandas as pd
import numpy as np
1.Pandas objects
在pandas中,有3种基本的数据结构,分别是Series,DataFrame 以及Index。我们来一一做一下介绍。
1.Series: 一个Series对象是一个一维的有索引数组,我们可以通过如下方式创建它。
s=pd.Series([0.25,0.5,0.75,1.0])
我们可以通过该对象的value和index属性来分别得到它的值以及它的索引。我们来看一下它们的结果。
s.values
# 返回结果为numpy array类型
array([0.25, 0.5 , 0.75, 1. ])
s.index
# 返回结果为pd.Index对象,也就是我上面所说的基本结构中的最后一种
RangeIndex(start=0, stop=4, step=1)
当然我们可以通过索引和切片来获取其中的部分值。比如
s[2]
# 返回结果
0.75
s[1:3]
# 返回结果
1 0.50
2 0.75
dtype: float64
在创建Series object的时候,我们也可以自定义我们需要的索引。
pd.Series([0.25, 0.5, 0.75, 1.0],index=['a', 'b', 'c', 'd'])
# 返回结果
a 0.25
b 0.50
c 0.75
d 1.00
dtype: float64
我们还可以通过传入字典的方式来创建,字典的key会成为索引,而value会成为值,比如下面这个人口数据的例子。
population_dict = {'California': 38332521,
'New York': 19651127,
'Florida': 19552860,
'Texas': 26448193,
'Illinois': 12882135}
population=pd.Series(population)
population
# 返回结果
California 38332521
New York 19651127
Florida 19552860
Texas 26448193
Illinois 12882135
dtype: int64
2.DataFrame:一个DataFrame 对象是一个二维的数组,同时拥有行索引和列名。我们也可以将其理解为一系列对齐的Series对象的组合。这里对齐代表这些Series对象共享行索引。
我们来看一下如何创建一个简单的DataFrame 对象。 1.通过给Series对象赋予列名,将其转化为一个DataFrame对象。我们使用上面我们创建的人口数的Series对象。
pd.DataFrame(population, columns=['population'])
# 返回结果
population
California 38332521
New York 19651127
Florida 19552860
Texas 26448193
Illinois 12882135
我们可以再创建一个名为area新的Series对象,然后将其和上面的population合并成一个名为state的字典,然后通过传入该字典来创建一个多列名的DataFrame对象。如下
area = pd.Series({'California': 423967, 'Texas': 695662, 'New York': 141297,'Florida': 170312, 'Illinois': 149995})
state={'population': population,'area': area}
pd.DataFrame(state)
# 返回结果
population area
California 38332521 423967
Florida 19552860 170312
Illinois 12882135 149995
New York 19651127 141297
Texas 26448193 695662
除了上述使用Series对象来创建DataFrame对象之外,我们还可以简单地通过传入list of dicts 或者 dict of lists的方式来创建。
# 1.list of dicts
data1=[{'a':i,'b':2*i} for i in range(3)]
pd.DataFrame(data1)
# 返回结果
a b
0 0 0
1 1 2
2 2 4
# 2.dict of lists
data2={'a':[i for i in range(3)],'b':[2*i for i in range(3)]}
pd.DataFrame(data2)
# 返回结果
a b
0 0 0
1 1 2
2 2 4
除此之外,我们也可以使用传入一个二维的numpy.array来创建DataFrame对象。
pd.DataFrame(np.random.rand(3, 2),
columns=['foo', 'bar'],
index=['a', 'b', 'c'])
# 返回结果
foo bar
a 0.550997 0.926800
b 0.536806 0.505545
c 0.766064 0.364634
最后我们简单介绍一下Index对象。
3.Index:我们在Series和DataFrame的介绍过程中其实已经反复看到Index的影子了,但是Index本身其实也是一个有趣的结构,我们既可以把它看作一个不可变的数组,也可以将其看作一种multi-set(可以包含重复值的集合),其支持python 内置数据结构set的基本操作,如&,|,^等。
idx=pd.Index([2,3,4,5,7])
idx
# 返回结果
Int64Index([2, 3, 5, 7, 11], dtype='int64')
# retrieve value
idx[1]
# slice
idx[::2]
# 但是我们不能去用一般的方法修改它。
idx[1]=0
将其当作一个集合。
idx1 = pd.Index([1, 3, 5, 7, 9])
idx2 = pd.Index([2, 3, 5, 7, 11])
idx1&idx2
# 返回结果
Int64Index([3, 5, 7], dtype='int64')
idx1|idx2
# 返回结果
Int64Index([1, 2, 3, 5, 7, 9, 11], dtype='int64')
idx1 ^ idx2
# 返回结果
Int64Index([1, 2, 9, 11], dtype='int64')
2.Data Indexing and Selection
这一部分,我们来聊一聊如何从pandas的数据对象中获取局部数据。我们主要以DataFrame为研究对象,因为大部分情况下,我们需要处理的数据都是具有多个columns的二维数据表。
我们还是使用在前一部分中使用过的人口与地区的DataFrame。
df=pd.DataFrame(state)
df
# 返回结果
population area
California 38332521 423967
Florida 19552860 170312
Illinois 12882135 149995
New York 19651127 141297
Texas 26448193 695662
1.列: 如果我们想简单地获取某列的数据,比如area, 可以直接使用df.area 或者 df['area']来获取。
2.行: 如果我们想获取某一行的值,比如第1行,可以简单地使用df.values[0],但是需要注意地是,该方法返回的结果只是一个numpy.array。
如果我们想使得返回的结果仍然是DataFrame的形式,还有两种更经常使用的方式,就是df.loc和df.iloc。第一种方式直接根据行名称和列名称进行对应的局部数据获取,而第二种方式则是通过索引来获取局部数据。我们来看两个具体的例子。
比如我想获取原始df中population列的前3个数据,那么使用df.loc方式就应该这么写
df.loc[:'Illinois','population']
而使用df.iloc就应该这么写
df.iloc[:3,0]
3.Basic Operation on Data
这一部分我们来聊一聊对于Pandas数据的基本操作。
1.我们知道Pandas在设计时就默认基于Numpy,因此Numpy的任何通用函数都可以用来对Pandas的对象进行操作,比如Series和DataFrame。我们来简单举个例子。
rng = np.random.RandomState(42) # 定义一个随机数生成器
# 定义一个随机二维数组形成的DataFrame
df = pd.DataFrame(rng.randint(0, 10, (3, 4)),
columns=['A', 'B', 'C', 'D'])
df
# 返回结果
A B C D
0 6 3 7 4
1 6 9 2 6
2 7 4 3 7
# 进行一个指数运算
np.exp(df)
# 返回结果
A B C D
0 403.428793 20.085537 1096.633158 54.598150
1 403.428793 8103.083928 7.389056 403.428793
2 1096.633158 54.598150 20.085537 1096.633158
2.如果我们对两个相同类型的结构进行二元运算,比如对两个DataFrame进行二元运算,pandas会自动帮我们进行索引对齐,我们以DataFrame作为研究对象来看一个例子。
# A是一个 2x2的数组转换而成的DataFrame
A = pd.DataFrame(rng.randint(0, 20, (2, 2)),
columns=list('AB'))
A
# 返回结果
A B
0 1 11
1 5 1
# B是一个 3x3的数组转换而成的DataFrame
B = pd.DataFrame(rng.randint(0, 10, (3, 3)),
columns=list('BAC'))
B
# 返回结果
B A C
0 4 0 9
1 5 8 0
2 9 2 6
# 然后我们对A,B进行+
A+B
# 返回结果
A B C
0 1.0 15.0 NaN
1 13.0 6.0 NaN
2 NaN NaN NaN
我们发现按照行索引和列名,pandas对象自动进行了对齐。但是还有一个问题就是因为两个DataFrame尺寸不匹配,导致NaN值的出现,因此,在Pandas中我们还可以使用另一种方法来填充NaN,即对小的DataFrame无法对齐的部分使用指定树值进行填充。
A.add(B,fill_value=0) # 对A进行0值填充,使其与B的shape一致
# 返回结果
A B C
0 1.0 15.0 9.0
1 13.0 6.0 0.0
2 2.0 9.0 6.0
下面这张表总结了Python中的基本操作以及对应的Pandas 方法。
3.如果我们对DataFrame与Series进行一些运算操作,其结果类似于一个二维的numpy.array和一个一维的numpy.array之间的运算,满足broadcasting机制。
举个例子如下。
df = pd.DataFrame(rng.randint(10, size=(3, 4)), columns=list('QRST'))
df
# 返回结果
Q R S T
0 1 7 5 1
1 4 0 9 5
2 8 0 9 2
df - df.iloc[0]
# 返回结果
Q R S T
0 0 0 0 0
1 3 -7 4 4
2 7 -7 4 1
因为在numpy中,二维数组与一维数组的差默认是在行维度上进行broadcasting的,因此pandas也是如此。如果想在列维度上进行相减,可以使用上面提到的pandas 二元操作方式,指定axis关键字。
# 比如减去R列
df.subtract(df['R'], axis=0)
# 返回结果
Q R S T
0 -6 0 -2 -6
1 4 0 9 5
2 8 0 9 2
4.Missing Values
缺失值的处理一直是数据分析中非常重要的一部分,在Python,我们使用None来代表缺失值或空值。除此之外,还有另一种缺失值表示,那就是NaN,这是一种特殊类型的浮点值,且其无法转换成int, string等其他数据类型。在Pandas的设计中,这两种缺失值都有被考虑到,因此Pandas能同时处理这两种缺失值,在Pandas中,这两种缺失值形式几乎是可交换的。
在Pandas中,我们有如下几个方法来处理缺失值。
1.isnull():返回一个boolean类型的mask来显示当前位置是否缺失值。
s = pd.Series([1, np.nan, 'hello', None])
s.isnull()
# 返回结果
0 False
1 True
2 False
3 True
dtype: bool
2.notnull():与isnull()刚好相反。
s.notnull()
# 返回结果
0 True
1 False
2 True
3 False
dtype: bool
3.dropna():即删除有缺失值的行或列,返回删除后的结果。
df = pd.DataFrame([[1, np.nan, 2],
[2, 3, 5],
[np.nan, 4, 6]])
df.dropna()
# 返回结果
0 1 2
1 2.0 3.0 5
我们可以看到,dropna()默认按行删除,同时只要一行种有一个nan,就会整行删除。事实上,我们也可以选择按列删除,即设定axis=1
df.dropna(axis=1)
# 返回结果
2
0 2
1 5
2 6
当然,我们还可以设定关键字how=‘all’,这样就只会删除那些整行或整列均为缺失值的行或列,默认为how='any’
4.fillna():填补缺失值,返回填补后的结果。在填补时,我们可以自定义填补值,比如0,也可以根据当前数据进行自动的前向或后项填补,即拷贝缺失值的前一个值或后一个值来进行填补,当然在DataFrame时,需要制定axis=0 或者 1
df
# 返回结果
0 1 2
0 1.0 NaN 2
1 2.0 3.0 5
2 NaN 4.0 6
# 后项填补
df.fillna(method='bfill', axis=1)
# 返回结果
0 1 2
0 1.0 2.0 2.0
1 2.0 3.0 5.0
2 4.0 4.0 6.0
5.Combining Datasets
合并数据集也是数据分析中非常重要的一个操作,在Pandas中有3中基本的join方式。
1.concat(),非常类似numpy中的concatenate()方法。以下是其参数。
pandas.concat(objs, axis=0, join=‘outer’, ignore_index=False, keys=None, levels=None, names=None, verify_integrity=False, sort=False, copy=True)
下面看几个例子
# 方便起见,这里定义一个快速生成DataFrame的函数
def make_df(cols, ind):
"""Quickly make a DataFrame"""
data = {c: [str(c) + str(i) for i in ind]
for c in cols}
return pd.DataFrame(data, ind)
df1 = make_df('AB', [1, 2])
df2 = make_df('AB', [3, 4])
# df1
A B
1 A1 B1
2 A2 B2
# df2
A B
3 A3 B3
4 A4 B4
# concat result
pd.concat([df1,df2]) # 默认在axis=0上拼接
A B
1 A1 B1
2 A2 B2
3 A3 B3
4 A4 B4
df3 = make_df('AB', [0, 1])
df4 = make_df('CD', [0, 1])
# df3
A B
0 A0 B0
1 A1 B1
# df4
C D
0 C0 D0
1 C1 D1
# concat result
pd.concat([df3, df4], axis=1) # 列维度拼接
A B C D
0 A0 B0 C0 D0
1 A1 B1 C1 D1
在Pandas 的concat中,默认保留两个数据集的索引,即使存在重复的情况。当你进行行维度合并时,这会产生问题,你可以在进行concat时,将ignore_index设置为True,当然前提是这个索引确实无关紧要。
# 首先默认保留索引
x = make_df('AB', [0, 1])
y = make_df('AB', [0, 1])
pd.concat([x, y])
# 返回结果
A B
0 A0 B0
1 A1 B1
0 A0 B0
1 A1 B1
pd.concat([x, y], ignore_index=True)
# 返回结果
A B
0 A0 B0
1 A1 B1
2 A0 B0
3 A1 B1
在上面的例子中,我们只关注了两个在行维度或列维度完全一致的数据集的拼接。但是事实上,我们真实遇到的数据集是不可能这么完美的。很多时候,我们会遇到columns不完全相同,但包含部分相同列的两个数据集。这个时候,按照默认的join,即outer的方式时,不共享的列会产生NaN,我们也可以使用内连接的方式,即值保留共享列。
df5 = make_df('ABC', [1, 5])
df6 = make_df('BCD', [3, 4])
# df5
A B C
1 A1 B1 C1
5 A5 B5 C5
# df6
B C D
3 B3 C3 D3
4 B4 C4 D4
pd.concat([df5, df6], sort=True)
# 返回结果
A B C D
1 A1 B1 C1 NaN
5 A5 B5 C5 NaN
3 NaN B3 C3 D3
4 NaN B4 C4 D4
# 使用内连接 inner
pd.concat([df5, df6], join='inner')
# 返回结果
B C
1 B1 C1
5 B5 C5
3 B3 C3
4 B4 C4
2.append():类似于Python列表的append()方法,即在尾端插入,但不同的是,在Pandas中,append()方法会创建一个新的对象,而不是修改原数据。因此重复多次的append()操作是低效的,会占用大量内存。这种情况,一般我们会采用将多个DataFrame一次放在一个list中,然后一次性传给concat()函数。
# 基本语法为
df1.append(df2)
3.merge():一种更加高效全面的数据集合并方法,也是我最常用的方法。如果你对SQL中的表连接熟悉的话,你会发现,merge非常类似。
首先,我们先从最基本的数据集合并开始,即两张表含有相同名字的column。one-to-one join 即两张表刚好有一个含有相同内容的column,顺序可以不同,merge会自动地去匹配。
df1 = pd.DataFrame({'employee': ['Bob', 'Jake', 'Lisa', 'Sue'],
'group': ['Accounting', 'Engineering', 'Engineering', 'HR']})
df2 = pd.DataFrame({'employee': ['Lisa', 'Bob', 'Jake', 'Sue'],
'hire_date': [2004, 2008, 2012, 2014]})
df3 = pd.merge(df1, df2)
# 返回结果
employee group hire_date
0 Bob Accounting 2008
1 Jake Engineering 2012
2 Lisa Engineering 2004
3 Sue HR 2014
many-to-one join 即两种表中的其中一个column包含重复值,merge方法会保留重复值,并对另一种表进行对应的填补。
df4 = pd.DataFrame({'group': ['Accounting', 'Engineering', 'HR'],
'supervisor': ['Carly', 'Guido', 'Steve']})
pd.merge(df3,df4)
# 返回结果
employee group hire_date supervisor
0 Bob Accounting 2008 Carly
1 Jake Engineering 2012 Guido
2 Lisa Engineering 2004 Guido
3 Sue HR 2014 Steve
还有一种就是many-to-many join,逻辑是完全一样的。
一般情况下,两种表的合并列往往不会完全同名,这个时候我们就需要借助on关键字了,确切地说是left_on和right_on。举个例子。
df1
# 返回结果
employee group
0 Bob Accounting
1 Jake Engineering
2 Lisa Engineering
3 Sue HR
df3 = pd.DataFrame({'name': ['Bob', 'Jake', 'Lisa', 'Sue'],
'salary': [70000, 80000, 120000, 90000]})
df3
# 返回结果
name salary
0 Bob 70000
1 Jake 80000
2 Lisa 120000
3 Sue 90000
pd.merge(df1, df3, left_on="employee", right_on="name") # employee 和 name 列 包含对应信息
# 返回结果
employee group name salary
0 Bob Accounting Bob 70000
1 Jake Engineering Jake 80000
2 Lisa Engineering Lisa 120000
3 Sue HR Sue 90000
但是合并后的结果会保留两个关键字列,我们可以使用.drop()任意删除一列。 有时候,我们也可以把关键字列作为索引,然后通过索引去合并,设置left_index=True and right_index=True。
OK,到目前为止,我们一直没有考虑一种常见的情况,即两张表中关键字列不同,这里说的不同指存在某个值只出现在某张表,没有出现在另一张表中。这个时候,我们需要考虑以哪张表作为基准,即合并方式how。和在SQL中一样,可选的方式有left/right/outer/inner,默认为inner。
最后一个问题就是当出现冲突列的情况,即两张表中的两个列名字相同,但是内容不同,此时合并的结果会默认将其中一列命名为col_x,另一列命名为col_y。我们也可以自定义,通过suffixes关键字。比如我们让suffixes=['_L','_R'],最后的结果就会变成col_L和col_R。
6.Hierarchical Indexing
到上面为止,我们只是考虑了一维或者二维的数据,但是,在Pandas中,事实上,我们也可以存储更高维度的数据,方法很简单,就是创建多维的索引。在Pandas中,我们可以使用MultiIndex方法来创建高维度的索引。以二维索引为例,如下。
# 首先创建index
index = [('California', 2000), ('California', 2010),
('New York', 2000), ('New York', 2010),
('Texas', 2000), ('Texas', 2010)]
# 创建一个简单的series 对象
populations = [33871648, 37253956,
18976457, 19378102,
20851820, 25145561]
pop = pd.Series(populations,index=index)
# 转换为MultiIndex对象
index = pd.MultiIndex.from_tuples(index)
# 将原始Series的index替换
pop=pop.reindex(index)
pop
# 返回结果
California 2000 33871648
2010 37253956
New York 2000 18976457
2010 19378102
Texas 2000 20851820
2010 25145561
dtype: int64
# 我们可以通过两个维度的索引来查询对应的数据
pop['Texas',2010]
# 返回结果
25145561
事实上,在上面的例子中,虽然只是一个Series,但是因为是二维的索引,因此很自然地我们可以将其转化为正常的二维表,也就是DataFrame,只需要通过.unstack()方法。
pop_df=pop.unstack()
pop_df
# 返回结果
2000 2010
California 33871648 37253956
New York 18976457 19378102
Texas 20851820 25145561
当然,.stack()方法提供了反向的转换过程。
如果我们想直接创建一个高维index的Series或者DataFrame,可以直接在index设置,即在一个列表中传入多个index子列表。比如
df = pd.DataFrame(np.random.rand(4, 2),
index=[['a', 'a', 'b', 'b'], [1, 2, 1, 2]],
columns=['data1', 'data2'])
df
# 返回结果
data1 data2
a 1 0.883948 0.144970
2 0.548344 0.475344
b 1 0.023430 0.038467
2 0.558775 0.531500
除了index可以设置高维度,columns同样可以设置高维度,这里就不展开了。
7.Aggregation and Grouping
终于,我们来到了数据分析最重要的环节之一,聚合操作。尤其是对于大量的数据,我们往往需要对它们从某个维度上进行分类,在Pandas中,我们需要用到的就是groupby。然后进行聚合操作,比如,求sum(),min(),max(),mean(),median()等等。除了这几个常见的聚合函数,还有如下的一系列聚合函数。
当 我们对一个DataFrame进行groupby时,返回的结果并不是一个DataFrame,而是DataFrameGroupBy 对象。你可以将其理解为DataFrame的一个特殊的view,它并没有在这一步完成真正的分组,真正的分组会在具体的聚合函数被应用时才会进行。即lazy evaluation。
OK,我们来具体看一看例子。
# 先创建一个简单的df
df = pd.DataFrame({'key': ['A', 'B', 'C', 'A', 'B', 'C'],
'data': range(6)}, columns=['key', 'data'])
df
# 返回结果
key data
0 A 0
1 B 1
2 C 2
3 A 3
4 B 4
5 C 5
# 按关键字key进行groupby
df.groupby('key')
# 返回结果
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x10eed9128>
# 我们可以使用apply方法来快速查看每组的数据个数
df.groupby('key').apply(lambda x:x.shape)
# 返回结果
key
A (2, 3)
B (2, 3)
C (2, 3)
dtype: object
# 使用describe()来看一看各个聚合函数的结果
df.groupby('key').describe()
# 返回结果
data
count mean std min 25% 50% 75% max
key
A 2.0 1.5 2.12132 0.0 0.75 1.5 2.25 3.0
B 2.0 2.5 2.12132 1.0 1.75 2.5 3.25 4.0
C 2.0 3.5 2.12132 2.0 2.75 3.5 4.25 5.0
# 如果我们想看某个或某几个聚合函数的结果,可以使用 aggregate,举例如下
df.groupby('key').aggregate(['min','max'])
# 返回结果
data
min max
key
A 0 3
B 1 4
C 2 5
这里注意一下,在向aggregate内传值时,它能接受的范围是很灵活的,可以传入字符串,比如我在示例中的'min','max',也可以直接传入min,max或者一个函数。
对于数据集的不同列,如果我们想要求不同的聚合结果,我们可以在aggregate中传入一个字典,其中key为列名,值则为我们想要应用的聚合函数。比如.aggregate({'data1':'max','data2':'min'}),就是对data1列求最大值,data2列求最小值。
除了aggregate,另一种能够与groupby搭配使用的方法是filter,比如我们可以根据聚合结果进行一些筛选。举个例子
rng = np.random.RandomState(0)
df = pd.DataFrame({'key': ['A', 'A', 'B', 'B', 'C', 'C'],
'data1': range(6),
'data2': rng.randint(0, 10, 6)},
columns = ['key', 'data1', 'data2'])
df
# 返回结果
key data1 data2
0 A 0 5
1 A 1 0
2 B 2 3
3 B 3 3
4 C 4 7
5 C 5 9
# 我们想要筛选出 data2 标准差大于1的部分数据
df.groupby('key').filter(lambda x: x['data2'].std() > 1)
# 返回结果
key data1 data2
0 A 0 5
1 A 1 0
4 C 4 7
5 C 5 9
另一种方法叫做transformation,顾名思义,就是对数据进行某种转换。举个例子
# 减去类均值。
df.groupby('key').transform(lambda x: x - x.mean())
# 返回结果
data1 data2
0 -0.5 2.5
1 0.5 -2.5
2 -0.5 0.0
3 0.5 0.0
4 -0.5 -1.0
5 0.5 1.0
还有最后一种最常见的方法叫做apply,apply方法不仅可以搭配groupby来使用,对于正常的DataFrame也可以进行任意形式的转换操作。在使用上,我们既可以在里面直接用匿名函数来定义转换逻辑,也可以传入外部函数名来进行转换。举个例子
def norm_by_data2(x):
# x is a DataFrame of group values
x['data1'] /= x['data2'].sum()
return x
df.groupby('key').apply(norm_by_data2)
# 返回结果
key data1 data2
0 A 0.000000 5
1 A 0.200000 0
2 B 0.333333 3
3 B 0.500000 3
4 C 0.250000 7
5 C 0.312500 9
唯一需要注意的一点是当我们将apply()方法与groupby搭配使用时,apply接收的x是一个DataFrame。
最后关于groupby还有一个知识点,那就是,除了将已有DataFrame的某列作为groupby的关键列之外,我们还可使用外部的序列或列表,只要它的长度与DataFrame一致。或者我们也可以构建一个字典作为映射表,即从DataFrame关键列到另一个外部值的映射。这时,我们groupby的结果也会根据这个外部值来进行。但是需要注意的是,我们需要先将这个关键列设置为index。举个例子
df2 = df.set_index('key')
df2
# 返回结果
data1 data2
key
A 0 5
A 1 0
B 2 3
B 3 3
C 4 7
C 5 9
mapping = {'A': 'vowel', 'B': 'consonant', 'C': 'consonant'}
df2.groupby(mapping).sum()
# 返回结果
data1 data2
consonant 14 22
vowel 1 5
8. Time Series Data
Pandas在处理时间数据时同时兼具Python的时间处理原生库datetime的灵活与易用性以及Numpy的时间数据类型datetime64的向量化操作的高效性。
1.使用to_datetime()解析高灵活性的日期输入
date=pd.to_datetime('24th of September,2021') # 今天日期
date
# 返回结果
Timestamp('2021-09-24 00:00:00') # 返回timestamp对象
2.使用to_timedelta()方法来进行类似numpy中的快速向量化操作。
date+pd.to_timedelta(np.arange(7), 'D')
# 返回结果
DatetimeIndex(['2021-09-24', '2021-09-25', '2021-09-26', '2021-09-27',
'2021-09-28', '2021-09-29', '2021-09-30'],
dtype='datetime64[ns]', freq=None)
# 我们还可以通过to_period()方法将DatetimeIndex对象转换为PeriodIndex对象
dates=date+pd.to_timedelta(np.arange(7), 'D')
dates.to_period('D')
# 返回结果
PeriodIndex(['2021-09-24', '2021-09-25', '2021-09-26', '2021-09-27',
'2021-09-28', '2021-09-29', '2021-09-30'],
dtype='period[D]', freq='D')
3.通过DatetimeIndex()来创建索引,用于构建Series或者DataFrame
index = pd.DatetimeIndex(['2021-09-24', '2021-09-25',
'2021-09-26', '2021-09-27'])
data = pd.Series([0, 1, 2, 3], index=index)
data
# 返回结果
2021-09-24 0
2021-09-25 1
2021-09-26 2
2021-09-27 3
dtype: int64
除此之外,我们还可以通过.date_range(),.period_range(),.timedelta_range()来自定义地创建不同的时间类型数据。比如
pd.date_range('2021-09-24',periods=14)
# 返回结果
DatetimeIndex(['2021-09-24', '2021-09-25', '2021-09-26', '2021-09-27',
'2021-09-28', '2021-09-29', '2021-09-30', '2021-10-01',
'2021-10-02', '2021-10-03', '2021-10-04', '2021-10-05',
'2021-10-06', '2021-10-07'],
dtype='datetime64[ns]', freq='D')
# 也可以设定freq
pd.date_range('2021-09-24',periods=14,freq=''W)
# 返回结果
DatetimeIndex(['2021-09-26', '2021-10-03', '2021-10-10', '2021-10-17',
'2021-10-24', '2021-10-31', '2021-11-07', '2021-11-14',
'2021-11-21', '2021-11-28', '2021-12-05', '2021-12-12',
'2021-12-19', '2021-12-26'],
dtype='datetime64[ns]', freq='W-SUN')
9. Pandas vs SQL
1.SELECT:选取特定的字段
在Pandas中,可以使用df[[col1,col2,...]]快速选取。
2.WHERE: 条件查询
在Pandas中,可以使用df[cond1 & cond2 ...]
3.GROUP BY:按组分类
在Pandas中,几乎完全一样的方式,即df.groupby(key_col)
4.JOIN:两表合并
在Pandas,推荐使用pd.merge(),在内部可以传入具体的连接方式,通过how关键字,比如left就是SQL中的左连接,即left join
5.UNION:记录拼接,在SQL中有分UNION 和 UNION ALL,UNION ALL 不去重
在Pandas中,我们可以通过pd.concat([df1,df2])来拼接,去重可以通过drop_duplicates()实现。
6.LIMIT/OFFSET:选取特定行数,比如从第5行开始的前10行 (DESC)
在Pandas中,我们可以使用df.nlargest(15,columns=col).tail(10) 更详细的对比,请参考:Comparison with SQL
10.总结
终于终于,这篇文章要结束了,对于像入门pandas的朋友,我相信这篇文章应该足够了,但是如果你想成为一个Python数据分析的专家,那么你还需要更多的实践以及在实践中继续探索Pandas更多的可能性。最后,感谢能够读完这篇文章的所有读者朋友。