学习所见的pandas操作总结

希望自己在兵荒马乱中保持韧性,发现宝藏。


目录

1.清除重复行或某个字段:drop_duplicates函数
2.apply函数
3.axis的理解
4.pd.read_csv()读数据
5.stack()和unstack()函数
6.groupby函数的使用
7.数据索引
8.数据排序
9.计算某列有多少个不同的值,类似sql中count(distinct A)
10.数据筛选,类似sql中like或where
11.将连续数据离散化操作或者类似sql中Case When
12.修改列名
13.map、apply、applymap详解
14.povit_table数据透视表的使用
15.数据表的合并和连接
16.全局替换dataframe.replace()
17.data.to_csv()

1.drop_duplicates

DataFrame.drop_duplicates(subset=None, keep='first', inplace=False)
subset: 列名,可选,默认为None
keep: {‘first’, ‘last’, False}, 默认值 ‘first’
first: 保留第一次出现的重复行,删除后面的重复行。
last: 删除重复项,除了最后一次出现。
False: 删除所有重复项。
inplace:布尔值,默认为False,是否直接在原数据上删除重复项或删除重复项后返回副本。(inplace=True表示直接在原来的DataFrame上删除重复项,而默认值False表示生成一个副本。)
根据数据的不同情况及处理数据的不同需求,通常会分为两种情况,一种是去除完全重复的行数据,另一种是去除某几列重复的行数据,就这两种情况可用下面的代码进行处理。

#去除完全重复的行数据
DataFrame.drop_duplicates()
#去除某几列重复的行数据
DataFrame.drop_duplicates(subset=['A','B'],keep='first')
#示例,删除positionId重复的行
data_duplicates=data.drop_duplicates(subset=['positionId'],keep='first')
data_duplicates.head()

2.apply函数

在pandas中,有Series和DataFrame两者数据类型。针对这两者类型使用apply函数有略微区别。

2.1 Series.apply
series = pd.Series([20, 21, 12], index=['London','New York','Helsinki'])
series
def square(x):
    x=x**2
    return x
series.apply(square)
series.apply(lambda x:x**2)
def subtract_custom_value(x, custom_value):
    return x-custom_value
series.apply(subtract_custom_value,custom_value=5)
def add_custom_values(x, **kwargs):
    for month in kwargs:
        x+=kwargs[month]
    return x
series.apply(add_custom_values,june=30, july=20, august=25)
series.apply(lambda x:np.log(x+1))
2.2 DataFrame.apply
df = pd.DataFrame([[4, 9,3],[3,5,2],[1,6,7]], columns=['A', 'B','C'])
df
df.apply(lambda x:x['A']+x['C'],axis=1)
df.apply(lambda x:np.sum(x),axis=0)

4.axis理解

axis是apply中的参数,axis=1为横向,axis=0为纵向,而不是行和列。这里axis=1就表示DataFrame根据lambda函数做横向计算。

4.pd.read_csv

使用pd.read_csv()读取数据,对于数据路径有三种办法。一是ipynb文件和使用的数据集在同一个文件中,此时可以直接读取。另一种就是ipynb文件和使用的数据集不在同一个文件中,此时读取数据需要使用相对路径和绝对路径。

#ipynb文件和使用的数据集在同一个文件中
data=pd.read_csv('DataAnalyst.csv',encoding='gbk')
#ipynb文件和使用的数据集不在同一个文件中,使用相对路径
data = pd.read_csv('C:/Users/Administrator/Desktop/秦路数据分析学习/第三周 Excel/DataAnalyst.csv',encoding='gbk')
#ipynb文件和使用的数据集不在同一个文件中,使用绝对路径
data = pd.read_csv('C:\\Users\\Administrator\\Desktop\\秦路数据分析学习\\第三周 Excel\\DataAnalyst.csv',encoding='gbk')

另外,pd.read_csv()的常用参数:
header:指定行数用来作为列名,数据开始行数。如果文件中没有列名,则默认为0,否则设置为None。
sep:默认的为',',即认为文件是以逗号分隔(一般csv文件都是这个格式),若是空格分隔,可以使用"\s+"。
names:用于结果的列名列表,对各列重命名,即添加表头。
如数据有表头,但想用新的表头,可以设置header=0,names=['a','b']实现表头定制。
encoding:一般默认为"utf-8",如果文件中有中文,一般需要使用"gbk"。

5.stack()和unstack()函数

对数据做处理时,经常需要对数据进行reshape。其中,stack和unstack是我们经常用到的操作之一。
stack和unstack是python进行层次化索引的重要操作。层次化索引就是对索引进行层次化分类,便于使用,这里的索引可以是行索引,也可以是列索引。

常见的数据的层次化结构有两种,一种是表格,一种是“花括号”,即下面这样的两种形式:
表格格式
花括号格式
表格在行列方向上均有索引,花括号结构只有“行方向”上的索引。
其实,应用stack和unstack只需要记住下面的知识点即可:

stack: 将数据从”表格结构“变成”花括号结构“,即将其列索引变成行索引。
unstack: 数据从”花括号结构“变成”表格结构“,即要将其中一层的行索引变成列索引。如果是多层索引,则以上函数是针对内层索引(这里是store)。利用level可以选择具体哪层索引。

import pandas as pd
import numpy as np
from pandas import Series,DataFrame
data=DataFrame(np.arange(12).reshape((3,4)),index=pd.Index(['street1','street2','street3']),
               columns=pd.Index(['store1','store2','store3','store4']))
print('----------data--------')
print(data)
print('-------------data2----------------------------\n')
data2=data.stack()
data3=data2.unstack()
print(data2)
print('--------------data3---------------------------\n')
print(data3)

data4=data2.unstack(level=0)
print('-------data4----------')
print(data4)
data5=data2.unstack(level=-1) # 默认的level=-1,内层的索引
print('------data5--------')
print(data5)
'''
----------data--------
         store1  store2  store3  store4
street1       0       1       2       3
street2       4       5       6       7
street3       8       9      10      11
-------------data2----------------------------

street1  store1     0
         store2     1
         store3     2
         store4     3
street2  store1     4
         store2     5
         store3     6
         store4     7
street3  store1     8
         store2     9
         store3    10
         store4    11
dtype: int64
--------------data3---------------------------

         store1  store2  store3  store4
street1       0       1       2       3
street2       4       5       6       7
street3       8       9      10      11
-------data4----------
        street1  street2  street3
store1        0        4        8
store2        1        5        9
store3        2        6       10
store4        3        7       11
------data5--------
         store1  store2  store3  store4
street1       0       1       2       3
street2       4       5       6       7
street3       8       9      10      11
'''

可以看到:使用stack函数,将data的列索引['store1','store2','store3’,'store4']转变成行索引(第二层),便得到了一个层次化的Series(data2),使用unstack函数,将data2的第二层行索引转变成列索引(默认内层索引,level=-1),便又得到了DataFrame(data3)
下面的例子我们利用level选择具体哪层索引。

data4=data2.unstack(level=0)
print(data4)
'''
        street1  street2  street3
store1        0        4        8
store2        1        5        9
store3        2        6       10
store4        3        7       11
'''

我们可以清晰看到,当我们取level=0时,即最外层索引时,unstack把行索引['street1','street2','street3’]变为了列索引。
重塑

6.groupby函数

#按照city和education分组
data_duplicates.groupby(['city','education']).mean()
#按city和education分组并计算bottomSalary最小值,最大值和平均值
data_duplicates.groupby(['city','education'])['bottomSalary'].agg(['min','max','mean'])
data_duplicates.groupby(['city','education']).agg({'bottomSalary':['mean','min'],'topSalary':'max','salary':'count'})

在日常的数据分析中,经常需要将数据根据某个(多个)字段划分为不同的群体(group)进行分析,如电商领域将全国的总销售额根据省份进行划分,分析各省销售额的变化情况,社交领域将用户根据画像(性别、年龄)进行细分,研究用户的使用情况和偏好等。在Pandas中,上述的数据处理操作主要运用groupby完成,在这里着重介绍一下groupby的基本原理及对应的agg、transform和apply操作。
为了后续图解的方便,采用模拟生成的10个样本数据,代码和数据如下:

company=["A","B","C"]
business=['X','Y','Z']
data=pd.DataFrame({
    "company":[company[x] for x in np.random.randint(0,len(company),10)],
    "business":[business[x] for x in np.random.randint(0,len(business),10)],
    "salary":np.random.randint(5,50,10),
    "age":np.random.randint(15,50,10)
}
)
data
1.Groupby的基本原理

在pandas中,实现分组操作的代码很简单,仅需一行代码,在这里,将上面的数据集按照company字段进行划分:

company_group=data.groupby(['company'])
company_group

将上述代码输入ipython后,会得到一个DataFrameGroupBy对象

那这个生成的DataFrameGroupBy是啥呢?对data进行了groupby后发生了什么?ipython所返回的结果是其内存地址,并不利于直观地理解,为了看看group内部究竟是什么,这里把group转换成list的形式来看一看:

list(company_group)
[('A',   company business  salary  age
  0       A        Z      37   21
  1       A        Y       5   44
  3       A        X      38   30
  4       A        Y      31   36
  9       A        X      28   17), 
 ('B',   company business  salary  age
  7       B        Y      15   37
  8       B        Y      15   18), 
 ('C',   company business  salary  age
  2       C        X      40   17
  5       C        Y      15   30
  6       C        Y      19   26)]

转换成列表的形式后,可以看到,列表由三个元组组成,每个元组中,第一个元素是组别(这里是按照company进行分组,所以最后分为了A,B,C),第二个元素的是对应组别下的DataFrame,整个过程可以图解如下:

总结来说,groupby的过程就是将原有的DataFrame按照groupby的字段(这里是company),划分为若干个分组DataFrame,被分为多少个组就有多少个分组DataFrame。所以说,在groupby之后的一系列操作(如agg、apply等),均是基于子DataFrame的操作。理解了这点,也就基本摸清了Pandas中groupby操作的主要原理。下面来讲讲groupby之后的常见操作。

2.agg聚合操作

聚合操作是groupby后非常常见的操作,会写SQL的朋友对此应该是非常熟悉了。聚合操作可以用来求和、均值、最大值、最小值等,下面的表格列出了Pandas中常见的聚合操作。

针对样例数据集,如果我想求不同公司员工的平均年龄和平均薪水,可以按照下方的代码进行:

data.groupby(['company']).mean()

如果想对针对不同的列求不同的值,比如要计算不同公司员工的平均年龄以及薪水的中位数,可以利用字典进行聚合操作的指定:

data.groupby(['company']).agg({'salary':'median','age':'mean'})
3.transform

transform是一种什么数据操作?和agg有什么区别呢?为了更好地理解transform和agg的不同,下面从实际的应用场景出发进行对比。
在上面的agg中,我们学会了如何求不同公司员工的平均薪水,如果现在需要在原数据集中新增一列avg_salary,代表员工所在的公司的平均薪水(相同公司的员工具有一样的平均薪水),该怎么实现呢?如果按照正常的步骤来计算,需要先求得不同公司的平均薪水,然后按照员工和公司的对应关系填充到对应的位置,不用transform的话,实现代码如下:

avg_salary=data.groupby(['company']).salary.mean().to_dict()
avg_salary
data['avgsalary'] = data['company'].map(avg_salary)
data

如果使用transform的话,仅需要一行代码:

data['avg_salary']=data.groupby(['company']).salary.transform('mean')
data

还是以图解的方式来看看进行groupby后transform的实现过程(为了更直观展示,图中加入了company列,实际按照上面的代码只有salary列):

图中的大方框是transform和agg所不一样的地方,对agg而言,会计算得到A,B,C公司对应的均值并直接返回,但对transform而言,则会对每一条数据求得相应的结果,同一组内的样本会有相同的值,组内求完均值后会按照原索引的顺序返回结果,如果有不理解的可以拿这张图和agg那张对比一下。

4.apply

apply相比agg和transform而言更加灵活,能够传入任意自定义的函数,实现复杂的数据操作。但apply在groupby后使用apply有什么特别的呢?
对于groupby后的apply,以分组后的子DataFrame作为参数传入指定函数的,基本操作单位是DataFrame,而一般apply的基本操作单位是Series。还是以一个案例来介绍groupby后的apply用法。
假设我现在需要获取各个公司年龄最大的员工的数据,该怎么实现呢?可以用以下代码实现:

def get_oldest_staff(x):
    df = x.sort_values(by='age',ascending=False)
    return df.iloc[0,:]
data.groupby('company',as_index=False).apply(get_oldest_staff)

这样便得到了每个公司年龄最大的员工的数据,整个流程图解如下:
可以看到,此处的apply和上篇文章中所介绍的作用原理基本一致,只是传入函数的参数由Series变为了此处的分组DataFrame。
最后,关于apply的使用,这里有个小建议,虽然说apply拥有更大的灵活性,但apply的运行效率会比agg和transform更慢。所以,groupby之后能用agg和transform解决的问题还是优先使用这两个方法,实在解决不了了才考虑使用apply进行操作。
知乎--Groupby函数

7.数据索引

#单独取出某一列
data_duplicates['city']
#取出多列
data_duplicates[['city','education']]
# 取出某列的某一行
data_duplicates['city'][0]
# 取出某列的某几行
data_duplicates['city'][:10]
#取出某几列的某几行
data_duplicates[['city','education','salary']][:1]

# loc方法索引
'''
DataFrame.loc[行名,列名]
'''
# 取出某几列的某几行
data_duplicates.loc[[0,1,5],['city','education','salary']]
# 取出 city ,education ,salary的0-20行所有索引名称为偶数的数据
data_duplicates.loc[0:21:2,['city','education','salary']]

#iloc方法索引
'''
DataFrame.iloc[行位置,列位置]
'''
#取出某几列的某几行
data_duplicates.iloc[[0,1,5],[0,1,2]]
# 取出前五列的奇数列的0-20行所有索引名称为偶数的数据
data_duplicates.iloc[0:21:2,0:5:2]

#ix方法索引
'''
DataFrame.ix[行位置/行名,列位置/列名]
'''
#筛选出A条件或B条件的样本。这里是组为实验组,页面为新页面或者组为控制组,页面为旧页面的样本
df1=df[((df['group']=='treatment')&(df['landing_page']=='new_page'))|((df['group']=='control')&(df['landing_page']=='old_page'))]

数据索引参考文章

8.数据排序

#Series.sort_values(ascending=False)False降序,True升序
data_duplicates['bottomSalary'].sort_values(ascending=False)
#df.sort_values(by=['A','B'],ascending=False)按by的字段进行排序
data_duplicates.sort_values(by='bottomSalary',ascending=False)
data_duplicates.sort_values(by=['bottomSalary','topSalary'],ascending=False)

9.计算某列有多少个不同的值,类似sql中count(distinct A)

data_duplicates['education'].nunique()

10.数据筛选,类似sql中like或where

#筛选教育背景要求本科及以上
data_duplicates[data_duplicates['education'].isin(['本科','硕士','博士'])]
#筛选受教育背景非大专的样本
data_duplicates[data_duplicates['education']!='大专']
#筛选公司名字中带直播的数据
data_duplicates[data_duplicates['companyShortName'].str.contains('直播')]
#筛选公司名字中带直播或者腾讯的数据
data_duplicates[data_duplicates['companyShortName'].str.contains('直播|腾讯')]
#多个条件筛选--在北京月薪10k起的直播行业招聘
data_duplicates[(data_duplicates['city']=='北京')&(data_duplicates['companyShortName'].str.contains('直播'))&(data_duplicates['bottomSalary']>10)]

11.将连续数据离散化操作或者类似sql中Case When

#根据最低薪资水平创建新的列SalaryType
#方法一
data_duplicates.loc[data_duplicates['bottomSalary']<=10,'SalaryType']='低薪资'
data_duplicates.loc[(data_duplicates['bottomSalary']>10)&(data_duplicates['bottomSalary']<=30),'SalaryType']='中等薪资'
data_duplicates.loc[data_duplicates['bottomSalary']>30,'SalaryType']='高薪资'
#方法二 pd.cut() 左开右闭 (0,10],(10,20],(20,1000]
data_duplicates['SalaryType1'] = pd.cut(data_duplicates['bottomSalary'],[0,10,20,1000],labels=['低薪资','中等薪资','高薪资'])

12.修改列名

data_duplicates.rename(columns={'city':'城市','公司名称':'companyname'},inplace=True)

13.map、apply、applymap详解

pandas数据处理的三板斧

在日常的数据处理中,经常会对一个DataFrame进行逐行、逐列和逐元素的操作,对应这些操作,Pandas中的map、apply和applymap可以解决绝大部分这样的数据处理需求。

boolean=[True,False]
gender=["男","女"]
color=["white","black","yellow"]
data=pd.DataFrame({
    "height":np.random.randint(150,190,100),
    "weight":np.random.randint(40,90,100),
    "smoker":[boolean[x] for x in np.random.randint(0,2,100)],
    "gender":[gender[x] for x in np.random.randint(0,2,100)],
    "age":np.random.randint(15,90,100),
    "color":[color[x] for x in np.random.randint(0,len(color),100) ]
})
data.head()
Series处理
1.map用法

如果需要把数据集中gender列的男替换为1,女替换为0,怎么做呢?绝对不是用for循环实现,使用Series.map()可以很容易做到,最少仅需一行代码。

#第一种方法
data['gender'] = data['gender'].map({'男':1,'女':0})
#第二种方法
#使用函数
def gender_map(x):  #这里x表示的是Series的一个元素,对Series元素依次处理
    if x=='男':
        gender=1
    else:
        gender=0
    return gender
data['gender']=data['gender'].map(gender_map)

那map在实际过程中是怎么运行的呢?请看下面的图解(为了方便展示,仅截取了前10条数据)

不论是利用字典还是函数进行映射,map方法都是把对应的数据逐个当作参数传入到字典或函数中,得到映射后的值。

2.apply用法

同时Series对象还有apply方法,apply方法的作用原理和map方法类似,区别在于apply能够传入功能更为复杂的函数。怎么理解呢?一起看看下面的例子。

假设在数据统计的过程中,年龄age列有较大误差,需要对其进行调整(加上或减去一个值),由于这个加上或减去的值未知,故在定义函数时,需要加多一个参数bias,此时用map方法是操作不了的(传入map的函数只能接收一个参数),apply方法则可以解决这个问题。

def apply_age(x,**kwargs):
    for i in kwargs:
        x+=kwargs[i]
    return x
#以字典的形式传递参数
data['age'] = data['age'].apply(apply_age,list_1=6,list_2=3,list_3=1)
data.head()

总而言之,对于Series而言,map可以解决绝大多数的数据处理需求,但如果需要使用较为复杂的函数,则需要用到apply方法。

DataFrame处理

对于DataFrame而言,基本没有DataFrame.map()的使用,map方法都是针对Series的处理。

1.apply

对DataFrame而言,apply是非常重要的数据处理方法,它可以接收各种各样的函数(Python内置的或自定义的),处理方式很灵活,下面通过几个例子来看看apply的具体使用及其原理。

在进行具体介绍之前,首先需要介绍一下DataFrame中axis的概念,在DataFrame对象的大多数方法中,都会有axis这个参数,它控制了你指定的操作是沿着0轴还是1轴进行。axis=0代表操作对列columns进行,axis=1代表操作对行row进行,如下图所示。
如果还不是很了解,没关系,下面会分别对apply沿着0轴以及1轴的操作进行讲解,继续往下走。

假设现在需要对data中的数值列分别进行取对数和求和的操作,这时可以用apply进行相应的操作,因为是对列进行操作,所以需要指定axis=0,使用下面的两行代码可以很轻松地解决我们的问题。

#沿着0轴求和
data[['height','weight','age']].apply(lambda x:sum(x),axis=0)
#沿着0轴求log
data[['height','weight','age']]=data[['height','weight','age']].apply(lambda x:np.log(x),axis=0)
data.head()

实现的方式很简单,但调用apply时究竟发生了什么呢?过程是怎么实现的?还是通过图解的方式来一探究竟。(取前五条数据为例)

当沿着轴0(axis=0)进行操作时,会将各列(columns)默认以Series的形式作为参数,传入到你指定的操作函数中,操作后合并并返回相应的结果。
那如果在实际使用中需要按行进行操作(axis=1),那整个过程又是怎么实现的呢?
在数据集中,有身高和体重的数据,所以根据这个,我们可以计算每个人的BMI指数(体检时常用的指标,衡量人体肥胖程度和是否健康的重要标准),计算公式是:体重指数BMI=体重/身高的平方(国际单位kg/㎡),因为需要对每个样本进行操作,这里使用axis=1的apply进行操作,代码如下:

def BMI(x):
    weight=x['weight']
    height=x['height']/100
    BMI = weight/height**2
    return BMI
data['BMI'] = data.apply(BMI,axis=1)
data.head()

当apply设置了axis=1对行进行操作时,会默认将每一行数据以Series的形式(Series的索引为列名)传入指定函数,返回相应的结果。
总结一下对DataFrame的apply操作:
1.当axis=0时,对每列columns执行指定函数;当axis=1时,对每行row执行指定函数。
2.无论axis=0还是axis=1,其传入指定函数的默认形式均为Series,可以通过设置raw=True传入numpy数组。
3.对每个Series执行结果后,会将结果整合在一起返回(若想有返回值,定义函数时需要return相应的值)
4.当然,DataFrame的apply和Series的apply一样,也能接收更复杂的函数,如传入参数等,实现原理是一样的,具体用法详见官方文档。

2.applymap

applymap的用法比较简单,会对DataFrame中的每个单元格执行指定函数的操作,虽然用途不如apply广泛,但在某些场合下还是比较有用的,如下面这个例子。

df = pd.DataFrame(
    {
        "A":np.random.randn(5),
        "B":np.random.randn(5),
        "C":np.random.randn(5),
        "D":np.random.randn(5),
        "E":np.random.randn(5),
    }
)
df

现在想将DataFrame中所有的值保留两位小数显示,使用applymap可以很快达到你想要的目的,代码和图解如下:

df.applymap(lambda x:'%.2f'%x)

14.pivot_table数据透视表

从功能上讲,Pandas 中用透视表 (pivot table) 和 Excel 里面的透视表是一样的。


pivot_table参数的含义

Pivot 字面意思是支点,即上图中的 index 和 columns 指定的行和列标签,支点可想理解成数据 (values) 在哪个维度上做整合 (aggfunc),再吧 NaN 值用 fill_value 替代,按行按列加总 (margin=True)。

boolean=[True,False]
gender=["男","女"]
color=["white","black","yellow"]
data=pd.DataFrame({
    "height":np.random.randint(150,190,100),
    "weight":np.random.randint(40,90,100),
    "smoker":[boolean[x] for x in np.random.randint(0,2,100)],
    "gender":[gender[x] for x in np.random.randint(0,2,100)],
    "age":np.random.randint(15,90,100),
    "color":[color[x] for x in np.random.randint(0,len(color),100) ]
})
data.head()
设置"单行"为pivot

创建透视表的 pivot_table() 函数里面的参数设置很多,学习它最有效的方式是每一步设置一个参数,检查结果是否符合预期。
先从最简单的语法开始,只设置 index='gender',通用语法如下:

data.pivot_table(index='gender')

从上表可以看到,年龄、身高、体重、甚至bool类型的是否吸烟都按某种聚合方式进行了合并了。现在大概可以猜出 pivot_table() 函数中有个参数用来设置整合方式,而默认值为平均。

设置"多行"为pivot

上例设置单个 index,接下来看看设置多个 index 的结果是什么样的。这时用列表来存储多个 index。通用语法如下:

data.pivot_table(index=['gender','smoker'])

到目前为止,我们只设置了 index,那为什么只在 age、height和weight三列上做整合呢?因为这两列的值是数值型 (int, float),而其他列的值是非数值型 (object)。

设定被整合的数据

如果只看 age列下的整合结果,只需设置 values='age' 或者 values=['age'],通用语法如下:
pd.pivot_table(df, index=label_list, values=label_list)

data.pivot_table(index=['gender','smoker'],values='age')
设定整合函数

默认整合函数是求平均,如果要用求和的函数需要设置 aggfunc=np.sum,通用语法为
pd.pivot_table(df, index=label_list, values=label_list, aggfunc=func)

data.pivot_table(index=['gender','smoker'],values='age',aggfunc='sum')

aggfunc 参数可以被设置为多个函数,用列表储存,通用语法为
pd.pivot_table(df, index=label_list, values=label_list, aggfunc=func_list)

data.pivot_table(index=['gender','smoker'],values='age',aggfunc=['sum','mean'])
设定列为pivot

如果进一步想看按吸烟划分后的整合结果,可以设置 columns=[''smoker"]

data.pivot_table(index=['gender'],columns=['smoker'],values='age',aggfunc=['mean'])
若表结果中有 NaN ,可设置 fill_value=0 用零替代。
要看总计怎么办?设置 margins=True 就可以了。
aggfunc 参数还可以传进一个字典来实现不同列下应用不同的整合函数,语法如下:

pd.pivot_table( df, index=["Counterparty","Trader","Category"], 
                    values=["Value","Quantity"],
                    aggfunc={"Value":np.sum, "Quantity":len},
                    fill_value=0,
                    margins=True )
再进一步,不同列还可以应用多个函数,只需把函数名称变成函数列表就可以了。语法如下:
pd.pivot_table( df, index=["Counterparty","Trader","Category"], 
                    values=["Value","Quantity"],
                    aggfunc={"Value":[np.sum, min, max], 
                             "Quantity":len},
                    fill_value=0 )

15.数据表的合并与连接

数据表可以按「键」合并,用 merge 函数;可以按「轴」来连接,用 concat 函数。

15.1合并

按键 (key) 合并可以分「单键合并」和「多键合并」。

单键合并

单键合并用merge函数,语法如下:
pd.merge(df1,df2,how=s,on=c)
c是df1和df2共有的一栏,合并方式(how=s)有四种:
1.左连接(left join):合并之后显示df1的所有行
2.右连接(right join):合并之后显示df2的所有行
3.外连接(outer join)合并df1和df2的所有行
4.内连接(inner join):合并所有行(默认情况)

首先创建两个 DataFrame:
df_price:4 天的价格 (2019-01-01 到 2019-01-04)
df_volume:5 天的交易量 (2019-01-02 到 2019-01-06)

df_price = pd.DataFrame( {'Date': pd.date_range('2019-1-1', periods=4),
                          'Adj Close': [24.42, 25.00, 25.25, 25.64]})
df_volume = pd.DataFrame( {'Date': pd.date_range('2019-1-2', periods=5),
                           'Volume' : [56081400, 99455500, 83028700, 100234000, 73829000]})

接下来用 df_price 和 df_volume 展示四种合并。
left join:

pd.merge(df_price,df_volume,how='left')


right join:

pd.merge(df_price,df_volume,how='right')


outer join:

pd.merge(df_price,df_volume,how='outer')


inner join:

pd.merge(df_price,df_volume,how='inner')
多键合并

多键合并用的语法和单键合并一样,只不过 on=c 中的 c 是多栏。
pd.merge( df1, df2, how=s, on=c )
首先创建两个 DataFrame:
portfolio1:3 比产品 FX Option, FX Swap 和 IR Option 的数量

portfolio2:4 比产品 FX Option (重复名称), FX Swap 和 IR Swap 的数量

porfolio1 = pd.DataFrame({'Asset': ['FX', 'FX', 'IR'], 
                          'Instrument': ['Option', 'Swap', 'Option'], 
                          'Number': [1, 2, 3]})
porfolio2 = pd.DataFrame({'Asset': ['FX', 'FX', 'FX', 'IR'], 
                          'Instrument': ['Option', 'Option', 'Swap', 'Swap'], 
                          'Number': [4, 5, 6, 7]})

pd.merge(porfolio1,porfolio2,on=['Asset','Instrument'],how='left')
15.2连接

Numpy 数组可相互连接,用 np.concat;同理,Series 也可相互连接,DataFrame 也可相互连接,用 pd.concat。

连接Series

在 concat 函数也可设定参数 axis:
axis = 0 (默认),沿着轴 0 (行) 连接,得到一个更长的 Series

axis = 1,沿着轴 1 (列) 连接,得到一个 DataFrame

被连接的 Series 它们的 index 可以重复 (overlapping),也可以不同。

s1 = pd.Series([0, 1], index=['a', 'b'])
s2 = pd.Series([2, 3, 4], index=['c', 'd', 'e'])
s3 = pd.Series([5, 6], index=['f', 'g'])
#沿着「轴 1」连接得到一个 DataFrame。
pd.concat([s1,s2,s3],axis=1)

将 s1 和 s3 沿「轴 0」连接来创建 s4,这样 s4 和 s1 的 index 是有重复的。

s4 = pd.concat([s1,s3],axis=0)
pd.concat([s1,s4],axis=1)
#将 s1 和 s4 沿「轴 1」内连接 (即只连接它们共有 index 对应的值)
pd.concat([s1,s4],axis=1,join='inner')
连接DataFrame

连接 DataFrame 的逻辑和连接 Series 的一模一样。
沿着行连接(axis=0):
先创建两个 DataFrame,df1 和 df2。

df1 = pd.DataFrame( np.arange(12).reshape(3,4), 
                    columns=['a','b','c','d'])
df2 = pd.DataFrame( np.arange(6).reshape(2,3),
                    columns=['b','d','a'])


沿着行连接分两步
1.先把 df1 和 df2 列标签补齐
2.再把 df1 和 df2 纵向连起来

pd.concat([df1,df2],axis=0,ignore_index=True)

沿着列连接(axis=1):
先创建两个 DataFrame,df1 和 df2。

df1 = pd.DataFrame( np.arange(6).reshape(3,2), 
                    index=['a','b','c'],
                    columns=['one','two'] )
df2 = pd.DataFrame( 5 + np.arange(4).reshape(2,2), 
                    index=['a','c'], 
                    columns=['three','four'])
pd.concat([df1,df2],axis=1)

16.全局替换DataFrame.replace(to_replace, value)

https://www.jianshu.com/p/2557a805211f
replace的基本结构是:df.replace(to_replace, value) 前面是需要替换的值,后面是替换后的值。
例如我们要将南岸改为城区:

image.png

这样Python就会搜索整个DataFrame并将文档中所有的南岸替换成了城区(要注意这样的操作并没有改变文档的源数据,要改变源数据需要使用inplace = True)。

17.data.to_csv()

不想要行索引。data.to_csv(index=False)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342