前言

作为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

python实现mongo数据库两个表数据对比 存在的删除并更新 不存在的直接插入_返回结果

将其当作一个集合。

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 方法。

python实现mongo数据库两个表数据对比 存在的删除并更新 不存在的直接插入_返回结果_02

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()等等。除了这几个常见的聚合函数,还有如下的一系列聚合函数。

python实现mongo数据库两个表数据对比 存在的删除并更新 不存在的直接插入_pandas_03

当 我们对一个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更多的可能性。最后,感谢能够读完这篇文章的所有读者朋友。