Pandas怎样对每个分组应用apply函数?

Pandas怎样对每个分组应用apply函数?

知识:Pandas的GroupBy遵从split、apply、combine模式

这里的split指的是pandas的groupby,我们自己实现apply函数,apply返回的结果由pandas进行combine得到结果

GroupBy.apply(function)

  • function的第一个参数是dataframe
  • function的返回结果,可是dataframe、series、单个值,甚至和输入dataframe完全没关系

本次实例演示:

  1. 怎样对数值列按分组的归一化?
  2. 怎样取每个分组的TOPN数据?

实例1:怎样对数值列按分组的归一化?

将不同范围的数值列进行归一化,映射到[0,1]区间:
* 更容易做数据横向对比,比如价格字段是几百到几千,增幅字段是0到100
* 机器学习模型学的更快性能更好

归一化的公式:

演示:用户对电影评分的归一化

每个用户的评分不同,有的乐观派评分高,有的悲观派评分低,按用户做归一化

import pandas as pd
ratings = pd.read_csv(
    "./datas/movielens-1m/ratings.dat", 
    sep="::",
    engine='python', 
    names="UserID::MovieID::Rating::Timestamp".split("::")
)
ratings.head()
UserIDMovieIDRatingTimestamp
0111935978300760
116613978302109
219143978301968
3134084978300275
4123555978824291
# 实现按照用户ID分组,然后对其中一列归一化
def ratings_norm(df):
    """
    @param df:每个用户分组的dataframe
    """
    min_value = df["Rating"].min()
    max_value = df["Rating"].max()
    df["Rating_norm"] = df["Rating"].apply(
        lambda x: (x-min_value)/(max_value-min_value))
    return df

ratings = ratings.groupby("UserID").apply(ratings_norm)
ratings[ratings["UserID"]==1].head()
UserIDMovieIDRatingTimestampRating_norm
01119359783007601.0
1166139783021090.0
2191439783019680.0
31340849783002750.5
41235559788242911.0

可以看到UserID==1这个用户,Rating==3是他的最低分,是个乐观派,我们归一化到0分;

实例2:怎样取每个分组的TOPN数据?

获取2018年每个月温度最高的2天数据

fpath = "./datas/beijing_tianqi/beijing_tianqi_2018.csv"
df = pd.read_csv(fpath)
# 替换掉温度的后缀℃
df.loc[:, "bWendu"] = df["bWendu"].str.replace("℃", "").astype('int32')
df.loc[:, "yWendu"] = df["yWendu"].str.replace("℃", "").astype('int32')
# 新增一列为月份
df['month'] = df['ymd'].str[:7]
df.head()
ymdbWenduyWendutianqifengxiangfengliaqiaqiInfoaqiLevelmonth
02018-01-013-6晴~多云东北风1-2级5922018-01
12018-01-022-5阴~多云东北风1-2级4912018-01
22018-01-032-5多云北风1-2级2812018-01
32018-01-040-8东北风1-2级2812018-01
42018-01-053-6多云~晴西北风1-2级5012018-01
def getWenduTopN(df, topn):
    """
    这里的df,是每个月份分组group的df
    """
    return df.sort_values(by="bWendu")[["ymd", "bWendu"]][-topn:]

df.groupby("month").apply(getWenduTopN, topn=1).head()
ymdbWendu
month
2018-01182018-01-197
2018-02562018-02-2612
2018-03852018-03-2727
2018-041182018-04-2930
2018-051502018-05-3135

我们看到,grouby的apply函数返回的dataframe,其实和原来的dataframe其实可以完全不一样

视频地址在:
http://www.iqiyi.com/a_19rrhyyqix.html

怎样使用Pandas的map和apply函数?

数据转换函数对比:map、apply、applymap:
1. map:只用于Series,实现每个值->值的映射;
2. apply:用于Series实现每个值的处理,用于Dataframe实现某个轴的Series的处理;
3. applymap:只能用于DataFrame,用于处理该DataFrame的每个元素;

1. map用于Series值的转换

实例:将股票代码英文转换成中文名字

Series.map(dict) or Series.map(function)均可

import pandas as pd
stocks = pd.read_excel('./datas/stocks/互联网公司股票.xlsx')
stocks.head()
日期公司收盘开盘交易量涨跌幅
02019-10-03BIDU104.32102.35104.73101.152.240.02
12019-10-02BIDU102.62100.85103.2499.502.690.01
22019-10-01BIDU102.00102.80103.26101.001.78-0.01
32019-10-03BABA169.48166.65170.18165.0010.390.02
42019-10-02BABA165.77162.82166.88161.9011.600.00
stocks["公司"].unique()
array(['BIDU', 'BABA', 'IQ', 'JD'], dtype=object)
# 公司股票代码到中文的映射,注意这里是小写
dict_company_names = {
    "bidu": "百度",
    "baba": "阿里巴巴",
    "iq": "爱奇艺", 
    "jd": "京东"
}

方法1:Series.map(dict)

stocks["公司中文1"] = stocks["公司"].str.lower().map(dict_company_names)
stocks.head()
日期公司收盘开盘交易量涨跌幅公司中文1
02019-10-03BIDU104.32102.35104.73101.152.240.02百度
12019-10-02BIDU102.62100.85103.2499.502.690.01百度
22019-10-01BIDU102.00102.80103.26101.001.78-0.01百度
32019-10-03BABA169.48166.65170.18165.0010.390.02阿里巴巴
42019-10-02BABA165.77162.82166.88161.9011.600.00阿里巴巴

方法2:Series.map(function)

function的参数是Series的每个元素的值

stocks["公司中文2"] = stocks["公司"].map(lambda x : dict_company_names[x.lower()])
stocks.head()
日期公司收盘开盘交易量涨跌幅公司中文1公司中文2
02019-10-03BIDU104.32102.35104.73101.152.240.02百度百度
12019-10-02BIDU102.62100.85103.2499.502.690.01百度百度
22019-10-01BIDU102.00102.80103.26101.001.78-0.01百度百度
32019-10-03BABA169.48166.65170.18165.0010.390.02阿里巴巴阿里巴巴
42019-10-02BABA165.77162.82166.88161.9011.600.00阿里巴巴阿里巴巴

2. apply用于Series和DataFrame的转换

  • Series.apply(function), 函数的参数是每个值
  • DataFrame.apply(function), 函数的参数是Series

Series.apply(function)

function的参数是Series的每个值

stocks["公司中文3"] = stocks["公司"].apply(
    lambda x : dict_company_names[x.lower()])
stocks.head()
日期公司收盘开盘交易量涨跌幅公司中文1公司中文2公司中文3
02019-10-03BIDU104.32102.35104.73101.152.240.02百度百度百度
12019-10-02BIDU102.62100.85103.2499.502.690.01百度百度百度
22019-10-01BIDU102.00102.80103.26101.001.78-0.01百度百度百度
32019-10-03BABA169.48166.65170.18165.0010.390.02阿里巴巴阿里巴巴阿里巴巴
42019-10-02BABA165.77162.82166.88161.9011.600.00阿里巴巴阿里巴巴阿里巴巴

DataFrame.apply(function)

function的参数是对应轴的Series

stocks["公司中文4"] = stocks.apply(
    lambda x : dict_company_names[x["公司"].lower()], 
    axis=1)

注意这个代码:
1、apply是在stocks这个DataFrame上调用;
2、lambda x的x是一个Series,因为指定了axis=1所以Seires的key是列名,可以用x[‘公司’]获取

stocks.head()
日期公司收盘开盘交易量涨跌幅公司中文1公司中文2公司中文3公司中文4
02019-10-03BIDU104.32102.35104.73101.152.240.02百度百度百度百度
12019-10-02BIDU102.62100.85103.2499.502.690.01百度百度百度百度
22019-10-01BIDU102.00102.80103.26101.001.78-0.01百度百度百度百度
32019-10-03BABA169.48166.65170.18165.0010.390.02阿里巴巴阿里巴巴阿里巴巴阿里巴巴
42019-10-02BABA165.77162.82166.88161.9011.600.00阿里巴巴阿里巴巴阿里巴巴阿里巴巴

3. applymap用于DataFrame所有值的转换

sub_df = stocks[['收盘', '开盘', '高', '低', '交易量']]
sub_df.head()
收盘开盘交易量
0104.32102.35104.73101.152.24
1102.62100.85103.2499.502.69
2102.00102.80103.26101.001.78
3169.48166.65170.18165.0010.39
4165.77162.82166.88161.9011.60
# 将这些数字取整数,应用于所有元素
sub_df.applymap(lambda x : int(x))
收盘开盘交易量
01041021041012
1102100103992
21021021031011
316916617016510
416516216616111
516516816816314
61615161510
7151515158
81516161511
9282828278
10282828279
112828282710
# 直接修改原df的这几列
stocks.loc[:, ['收盘', '开盘', '高', '低', '交易量']] = sub_df.applymap(lambda x : int(x))
stocks.head()
日期公司收盘开盘交易量涨跌幅公司中文1公司中文2公司中文3公司中文4
02019-10-03BIDU10410210410120.02百度百度百度百度
12019-10-02BIDU1021001039920.01百度百度百度百度
22019-10-01BIDU1021021031011-0.01百度百度百度百度
32019-10-03BABA169166170165100.02阿里巴巴阿里巴巴阿里巴巴阿里巴巴
42019-10-02BABA165162166161110.00阿里巴巴阿里巴巴阿里巴巴阿里巴巴

Pandas的分层索引MultiIndex怎样用?

Pandas的分层索引MultiIndex

为什么要学习分层索引MultiIndex?
* 分层索引:在一个轴向上拥有多个索引层级,可以表达更高维度数据的形式;
* 可以更方便的进行数据筛选,如果有序则性能更好;
* groupby等操作的结果,如果是多KEY,结果是分层索引,需要会使用
* 一般不需要自己创建分层索引(MultiIndex有构造函数但一般不用)

演示数据:百度、阿里巴巴、爱奇艺、京东四家公司的10天股票数据
数据来自:英为财经
https://cn.investing.com/

本次演示提纲:
一、Series的分层索引MultiIndex
二、Series有多层索引怎样筛选数据?
三、DataFrame的多层索引MultiIndex
四、DataFrame有多层索引怎样筛选数据?

import pandas as pd
%matplotlib inline
stocks = pd.read_excel('./datas/stocks/互联网公司股票.xlsx')
stocks.shape
(12, 8)
stocks.head(3)
日期公司收盘开盘交易量涨跌幅
02019-10-03BIDU104.32102.35104.73101.152.240.02
12019-10-02BIDU102.62100.85103.2499.502.690.01
22019-10-01BIDU102.00102.80103.26101.001.78-0.01
stocks["公司"].unique()
array(['BIDU', 'BABA', 'IQ', 'JD'], dtype=object)
stocks.index
RangeIndex(start=0, stop=12, step=1)
stocks.groupby('公司')["收盘"].mean()
公司
BABA    166.80
BIDU    102.98
IQ       15.90
JD       28.35
Name: 收盘, dtype: float64

一、Series的分层索引MultiIndex

ser = stocks.groupby(['公司', '日期'])['收盘'].mean()
ser
公司    日期        
BABA  2019-10-01    165.15
      2019-10-02    165.77
      2019-10-03    169.48
BIDU  2019-10-01    102.00
      2019-10-02    102.62
      2019-10-03    104.32
IQ    2019-10-01     15.92
      2019-10-02     15.72
      2019-10-03     16.06
JD    2019-10-01     28.19
      2019-10-02     28.06
      2019-10-03     28.80
Name: 收盘, dtype: float64

多维索引中,空白的意思是:使用上面的值

ser.index
MultiIndex([('BABA', '2019-10-01'),
            ('BABA', '2019-10-02'),
            ('BABA', '2019-10-03'),
            ('BIDU', '2019-10-01'),
            ('BIDU', '2019-10-02'),
            ('BIDU', '2019-10-03'),
            (  'IQ', '2019-10-01'),
            (  'IQ', '2019-10-02'),
            (  'IQ', '2019-10-03'),
            (  'JD', '2019-10-01'),
            (  'JD', '2019-10-02'),
            (  'JD', '2019-10-03')],
           names=['公司', '日期'])
# unstack把二级索引变成列
ser.unstack()
日期2019-10-012019-10-022019-10-03
公司
BABA165.15165.77169.48
BIDU102.00102.62104.32
IQ15.9215.7216.06
JD28.1928.0628.80
ser
公司    日期        
BABA  2019-10-01    165.15
      2019-10-02    165.77
      2019-10-03    169.48
BIDU  2019-10-01    102.00
      2019-10-02    102.62
      2019-10-03    104.32
IQ    2019-10-01     15.92
      2019-10-02     15.72
      2019-10-03     16.06
JD    2019-10-01     28.19
      2019-10-02     28.06
      2019-10-03     28.80
Name: 收盘, dtype: float64
ser.reset_index()
公司日期收盘
0BABA2019-10-01165.15
1BABA2019-10-02165.77
2BABA2019-10-03169.48
3BIDU2019-10-01102.00
4BIDU2019-10-02102.62
5BIDU2019-10-03104.32
6IQ2019-10-0115.92
7IQ2019-10-0215.72
8IQ2019-10-0316.06
9JD2019-10-0128.19
10JD2019-10-0228.06
11JD2019-10-0328.80

二、Series有多层索引MultiIndex怎样筛选数据?

ser
公司    日期        
BABA  2019-10-01    165.15
      2019-10-02    165.77
      2019-10-03    169.48
BIDU  2019-10-01    102.00
      2019-10-02    102.62
      2019-10-03    104.32
IQ    2019-10-01     15.92
      2019-10-02     15.72
      2019-10-03     16.06
JD    2019-10-01     28.19
      2019-10-02     28.06
      2019-10-03     28.80
Name: 收盘, dtype: float64
ser.loc['BIDU']
日期
2019-10-01    102.00
2019-10-02    102.62
2019-10-03    104.32
Name: 收盘, dtype: float64
# 多层索引,可以用元组的形式筛选
ser.loc[('BIDU', '2019-10-02')]
102.62
ser.loc[:, '2019-10-02']
公司
BABA    165.77
BIDU    102.62
IQ       15.72
JD       28.06
Name: 收盘, dtype: float64

三、DataFrame的多层索引MultiIndex

stocks.head()
日期公司收盘开盘交易量涨跌幅
02019-10-03BIDU104.32102.35104.73101.152.240.02
12019-10-02BIDU102.62100.85103.2499.502.690.01
22019-10-01BIDU102.00102.80103.26101.001.78-0.01
32019-10-03BABA169.48166.65170.18165.0010.390.02
42019-10-02BABA165.77162.82166.88161.9011.600.00
stocks.set_index(['公司', '日期'], inplace=True)
stocks
收盘开盘交易量涨跌幅
公司日期
BIDU2019-10-03104.32102.35104.73101.152.240.02
2019-10-02102.62100.85103.2499.502.690.01
2019-10-01102.00102.80103.26101.001.78-0.01
BABA2019-10-03169.48166.65170.18165.0010.390.02
2019-10-02165.77162.82166.88161.9011.600.00
2019-10-01165.15168.01168.23163.6414.19-0.01
IQ2019-10-0316.0615.7116.3815.3210.080.02
2019-10-0215.7215.8515.8715.128.10-0.01
2019-10-0115.9216.1416.2215.5011.65-0.01
JD2019-10-0328.8028.1128.9727.828.770.03
2019-10-0228.0628.0028.2227.539.530.00
2019-10-0128.1928.2228.5727.9710.640.00
stocks.index
MultiIndex([('BIDU', '2019-10-03'),
            ('BIDU', '2019-10-02'),
            ('BIDU', '2019-10-01'),
            ('BABA', '2019-10-03'),
            ('BABA', '2019-10-02'),
            ('BABA', '2019-10-01'),
            (  'IQ', '2019-10-03'),
            (  'IQ', '2019-10-02'),
            (  'IQ', '2019-10-01'),
            (  'JD', '2019-10-03'),
            (  'JD', '2019-10-02'),
            (  'JD', '2019-10-01')],
           names=['公司', '日期'])
stocks.sort_index(inplace=True)
stocks
收盘开盘交易量涨跌幅
公司日期
BABA2019-10-01165.15168.01168.23163.6414.19-0.01
2019-10-02165.77162.82166.88161.9011.600.00
2019-10-03169.48166.65170.18165.0010.390.02
BIDU2019-10-01102.00102.80103.26101.001.78-0.01
2019-10-02102.62100.85103.2499.502.690.01
2019-10-03104.32102.35104.73101.152.240.02
IQ2019-10-0115.9216.1416.2215.5011.65-0.01
2019-10-0215.7215.8515.8715.128.10-0.01
2019-10-0316.0615.7116.3815.3210.080.02
JD2019-10-0128.1928.2228.5727.9710.640.00
2019-10-0228.0628.0028.2227.539.530.00
2019-10-0328.8028.1128.9727.828.770.03

四、DataFrame有多层索引MultiIndex怎样筛选数据?

重要知识】在选择数据时:
* 元组(key1,key2)代表筛选多层索引,其中key1是索引第一级,key2是第二级,比如key1=JD, key2=2019-10-02
* 列表[key1,key2]代表同一层的多个KEY,其中key1和key2是并列的同级索引,比如key1=JD, key2=BIDU

stocks.loc['BIDU']
收盘开盘交易量涨跌幅
日期
2019-10-01102.00102.80103.26101.001.78-0.01
2019-10-02102.62100.85103.2499.502.690.01
2019-10-03104.32102.35104.73101.152.240.02
stocks.loc[('BIDU', '2019-10-02'), :]
收盘     102.62
开盘     100.85
高      103.24
低       99.50
交易量      2.69
涨跌幅      0.01
Name: (BIDU, 2019-10-02), dtype: float64
stocks.loc[('BIDU', '2019-10-02'), '开盘']
100.85
stocks.loc[['BIDU', 'JD'], :]
收盘开盘交易量涨跌幅
公司日期
BIDU2019-10-01102.00102.80103.26101.001.78-0.01
2019-10-02102.62100.85103.2499.502.690.01
2019-10-03104.32102.35104.73101.152.240.02
JD2019-10-0128.1928.2228.5727.9710.640.00
2019-10-0228.0628.0028.2227.539.530.00
2019-10-0328.8028.1128.9727.828.770.03
stocks.loc[(['BIDU', 'JD'], '2019-10-03'), :]
收盘开盘交易量涨跌幅
公司日期
BIDU2019-10-03104.32102.35104.73101.152.240.02
JD2019-10-0328.8028.1128.9727.828.770.03
stocks.loc[(['BIDU', 'JD'], '2019-10-03'), '收盘']
公司    日期        
BIDU  2019-10-03    104.32
JD    2019-10-03     28.80
Name: 收盘, dtype: float64
stocks.loc[('BIDU', ['2019-10-02', '2019-10-03']), '收盘']
公司    日期        
BIDU  2019-10-02    102.62
      2019-10-03    104.32
Name: 收盘, dtype: float64
# slice(None)代表筛选这一索引的所有内容
stocks.loc[(slice(None), ['2019-10-02', '2019-10-03']), :]
收盘开盘交易量涨跌幅
公司日期
BABA2019-10-02165.77162.82166.88161.9011.600.00
2019-10-03169.48166.65170.18165.0010.390.02
BIDU2019-10-02102.62100.85103.2499.502.690.01
2019-10-03104.32102.35104.73101.152.240.02
IQ2019-10-0215.7215.8515.8715.128.10-0.01
2019-10-0316.0615.7116.3815.3210.080.02
JD2019-10-0228.0628.0028.2227.539.530.00
2019-10-0328.8028.1128.9727.828.770.03
stocks.reset_index()
公司日期收盘开盘交易量涨跌幅
0BABA2019-10-01165.15168.01168.23163.6414.19-0.01
1BABA2019-10-02165.77162.82166.88161.9011.600.00
2BABA2019-10-03169.48166.65170.18165.0010.390.02
3BIDU2019-10-01102.00102.80103.26101.001.78-0.01
4BIDU2019-10-02102.62100.85103.2499.502.690.01
5BIDU2019-10-03104.32102.35104.73101.152.240.02
6IQ2019-10-0115.9216.1416.2215.5011.65-0.01
7IQ2019-10-0215.7215.8515.8715.128.10-0.01
8IQ2019-10-0316.0615.7116.3815.3210.080.02
9JD2019-10-0128.1928.2228.5727.9710.640.00
10JD2019-10-0228.0628.0028.2227.539.530.00
11JD2019-10-0328.8028.1128.9727.828.770.03

转载请注明链接: http://www.crazyant.net/2598.html

Pandas怎样实现对数据的分组统计?

Pandas怎样实现groupby分组统计

类似SQL:
select city,max(temperature) from city_weather group by city;

groupby:先对数据分组,然后在每个分组上应用聚合函数、转换函数

本次演示:
一、分组使用聚合函数做数据统计
二、遍历groupby的结果理解执行流程
三、实例分组探索天气数据

import pandas as pd
import numpy as np
# 加上这一句,能在jupyter notebook展示matplot图表
%matplotlib inline
df = pd.DataFrame({'A': ['foo', 'bar', 'foo', 'bar', 'foo', 'bar', 'foo', 'foo'],
                   'B': ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'],
                   'C': np.random.randn(8),
                   'D': np.random.randn(8)})
df
ABCD
0fooone0.5429030.788896
1barone-0.375789-0.345869
2footwo-0.9034070.428031
3barthree-1.5647480.081163
4footwo-1.0936020.837348
5bartwo-0.2024030.701301
6fooone-0.665189-1.505290
7foothree-0.4983390.534438

一、分组使用聚合函数做数据统计

1、单个列groupby,查询所有数据列的统计

df.groupby('A').sum()
CD
A
bar-2.1429400.436595
foo-2.6176331.083423

我们看到:
1. groupby中的’A’变成了数据的索引列
2. 因为要统计sum,但B列不是数字,所以被自动忽略掉

2、多个列groupby,查询所有数据列的统计

df.groupby(['A','B']).mean()
CD
AB
barone-0.375789-0.345869
three-1.5647480.081163
two-0.2024030.701301
fooone-0.061143-0.358197
three-0.4983390.534438
two-0.9985040.632690

我们看到:(‘A’,’B’)成对变成了二级索引

df.groupby(['A','B'], as_index=False).mean()
ABCD
0barone-0.375789-0.345869
1barthree-1.5647480.081163
2bartwo-0.2024030.701301
3fooone-0.061143-0.358197
4foothree-0.4983390.534438
5footwo-0.9985040.632690

3、同时查看多种数据统计

df.groupby('A').agg([np.sum, np.mean, np.std])
CD
summeanstdsummeanstd
A
bar-2.142940-0.7143130.7415830.4365950.1455320.526544
foo-2.617633-0.5235270.6378221.0834230.2166850.977686

我们看到:列变成了多级索引

4、查看单列的结果数据统计

# 方法1:预过滤,性能更好
df.groupby('A')['C'].agg([np.sum, np.mean, np.std])
summeanstd
A
bar-2.142940-0.7143130.741583
foo-2.617633-0.5235270.637822
# 方法2
df.groupby('A').agg([np.sum, np.mean, np.std])['C']
summeanstd
A
bar-2.142940-0.7143130.741583
foo-2.617633-0.5235270.637822

5、不同列使用不同的聚合函数

df.groupby('A').agg({"C":np.sum, "D":np.mean})
CD
A
bar-2.1429400.145532
foo-2.6176330.216685

二、遍历groupby的结果理解执行流程

for循环可以直接遍历每个group

1、遍历单个列聚合的分组
g = df.groupby('A')
g
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x00000123B250E548>
for name,group in g:
    print(name)
    print(group)
    print()
bar
     A      B         C         D
1  bar    one -0.375789 -0.345869
3  bar  three -1.564748  0.081163
5  bar    two -0.202403  0.701301

foo
     A      B         C         D
0  foo    one  0.542903  0.788896
2  foo    two -0.903407  0.428031
4  foo    two -1.093602  0.837348
6  foo    one -0.665189 -1.505290
7  foo  three -0.498339  0.534438

可以获取单个分组的数据

g.get_group('bar')
ABCD
1barone-0.375789-0.345869
3barthree-1.5647480.081163
5bartwo-0.2024030.701301
2、遍历多个列聚合的分组
g = df.groupby(['A', 'B'])
for name,group in g:
    print(name)
    print(group)
    print()
('bar', 'one')
     A    B         C         D
1  bar  one -0.375789 -0.345869

('bar', 'three')
     A      B         C         D
3  bar  three -1.564748  0.081163

('bar', 'two')
     A    B         C         D
5  bar  two -0.202403  0.701301

('foo', 'one')
     A    B         C         D
0  foo  one  0.542903  0.788896
6  foo  one -0.665189 -1.505290

('foo', 'three')
     A      B         C         D
7  foo  three -0.498339  0.534438

('foo', 'two')
     A    B         C         D
2  foo  two -0.903407  0.428031
4  foo  two -1.093602  0.837348

可以看到,name是一个2个元素的tuple,代表不同的列

g.get_group(('foo', 'one'))
ABCD
0fooone0.5429030.788896
6fooone-0.665189-1.505290

可以直接查询group后的某几列,生成Series或者子DataFrame

g['C']
<pandas.core.groupby.generic.SeriesGroupBy object at 0x00000123C33F64C8>
for name, group in g['C']:
    print(name)
    print(group)
    print(type(group))
    print()
('bar', 'one')
1   -0.375789
Name: C, dtype: float64
<class 'pandas.core.series.Series'>

('bar', 'three')
3   -1.564748
Name: C, dtype: float64
<class 'pandas.core.series.Series'>

('bar', 'two')
5   -0.202403
Name: C, dtype: float64
<class 'pandas.core.series.Series'>

('foo', 'one')
0    0.542903
6   -0.665189
Name: C, dtype: float64
<class 'pandas.core.series.Series'>

('foo', 'three')
7   -0.498339
Name: C, dtype: float64
<class 'pandas.core.series.Series'>

('foo', 'two')
2   -0.903407
4   -1.093602
Name: C, dtype: float64
<class 'pandas.core.series.Series'>

其实所有的聚合统计,都是在dataframe和series上进行的;

三、实例分组探索天气数据

fpath = "./datas/beijing_tianqi/beijing_tianqi_2018.csv"
df = pd.read_csv(fpath)
# 替换掉温度的后缀℃
df.loc[:, "bWendu"] = df["bWendu"].str.replace("℃", "").astype('int32')
df.loc[:, "yWendu"] = df["yWendu"].str.replace("℃", "").astype('int32')
df.head()
ymdbWenduyWendutianqifengxiangfengliaqiaqiInfoaqiLevel
02018-01-013-6晴~多云东北风1-2级592
12018-01-022-5阴~多云东北风1-2级491
22018-01-032-5多云北风1-2级281
32018-01-040-8东北风1-2级281
42018-01-053-6多云~晴西北风1-2级501
# 新增一列为月份
df['month'] = df['ymd'].str[:7]
df.head()
ymdbWenduyWendutianqifengxiangfengliaqiaqiInfoaqiLevelmonth
02018-01-013-6晴~多云东北风1-2级5922018-01
12018-01-022-5阴~多云东北风1-2级4912018-01
22018-01-032-5多云北风1-2级2812018-01
32018-01-040-8东北风1-2级2812018-01
42018-01-053-6多云~晴西北风1-2级5012018-01

1、查看每个月的最高温度

data = df.groupby('month')['bWendu'].max()
data
month
2018-01     7
2018-02    12
2018-03    27
2018-04    30
2018-05    35
2018-06    38
2018-07    37
2018-08    36
2018-09    31
2018-10    25
2018-11    18
2018-12    10
Name: bWendu, dtype: int32
type(data)
pandas.core.series.Series
data.plot()
<matplotlib.axes._subplots.AxesSubplot at 0x123c344b308>

2、查看每个月的最高温度、最低温度、平均空气质量指数

df.head()
ymdbWenduyWendutianqifengxiangfengliaqiaqiInfoaqiLevelmonth
02018-01-013-6晴~多云东北风1-2级5922018-01
12018-01-022-5阴~多云东北风1-2级4912018-01
22018-01-032-5多云北风1-2级2812018-01
32018-01-040-8东北风1-2级2812018-01
42018-01-053-6多云~晴西北风1-2级5012018-01
group_data = df.groupby('month').agg({"bWendu":np.max, "yWendu":np.min, "aqi":np.mean})
group_data
bWenduyWenduaqi
month
2018-017-1260.677419
2018-0212-1078.857143
2018-0327-4130.322581
2018-04301102.866667
2018-05351099.064516
2018-06381782.300000
2018-07372272.677419
2018-08362059.516129
2018-09311150.433333
2018-1025167.096774
2018-1118-4105.100000
2018-1210-1277.354839
group_data.plot()
<matplotlib.axes._subplots.AxesSubplot at 0x123c5502d48>


怎样使用Pandas批量拆分与合并Excel文件?

Pandas批量拆分Excel与合并Excel

实例演示:
1. 将一个大Excel等份拆成多个Excel
2. 将多个小Excel合并成一个大Excel并标记来源

work_dir="./course_datas/c15_excel_split_merge"
splits_dir=f"{work_dir}/splits"

import os
if not os.path.exists(splits_dir):
    os.mkdir(splits_dir)

0、读取源Excel到Pandas

import pandas as pd
df_source = pd.read_excel(f"{work_dir}/crazyant_blog_articles_source.xlsx")
df_source.head()
idtitletags
02585Tensorflow怎样接收变长列表特征python,tensorflow,特征工程
12583Pandas实现数据的合并concatpandas,python,数据分析
22574Pandas的Index索引有什么用途?pandas,python,数据分析
32564机器学习常用数据集大全python,机器学习
42561一个数据科学家的修炼路径数据分析
df_source.index
RangeIndex(start=0, stop=258, step=1)
df_source.shape
(258, 3)
total_row_count = df_source.shape[0]
total_row_count
258

一、将一个大Excel等份拆成多个Excel

  1. 使用df.iloc方法,将一个大的dataframe,拆分成多个小dataframe
  2. 将使用dataframe.to_excel保存每个小Excel

1、计算拆分后的每个excel的行数

# 这个大excel,会拆分给这几个人
user_names = ["xiao_shuai", "xiao_wang", "xiao_ming", "xiao_lei", "xiao_bo", "xiao_hong"]
# 每个人的任务数目
split_size = total_row_count // len(user_names)
if total_row_count % len(user_names) != 0:
    split_size += 1

split_size
43

2、拆分成多个dataframe

df_subs = []
for idx, user_name in enumerate(user_names):
    # iloc的开始索引
    begin = idx*split_size
    # iloc的结束索引
    end = begin+split_size
    # 实现df按照iloc拆分
    df_sub = df_source.iloc[begin:end]
    # 将每个子df存入列表
    df_subs.append((idx, user_name, df_sub))

3、将每个datafame存入excel

for idx, user_name, df_sub in df_subs:
    file_name = f"{splits_dir}/crazyant_blog_articles_{idx}_{user_name}.xlsx"
    df_sub.to_excel(file_name, index=False)

二、合并多个小Excel到一个大Excel

  1. 遍历文件夹,得到要合并的Excel文件列表
  2. 分别读取到dataframe,给每个df添加一列用于标记来源
  3. 使用pd.concat进行df批量合并
  4. 将合并后的dataframe输出到excel

1. 遍历文件夹,得到要合并的Excel名称列表

import os
excel_names = []
for excel_name in os.listdir(splits_dir):
    excel_names.append(excel_name)
excel_names
['crazyant_blog_articles_0_xiao_shuai.xlsx',
 'crazyant_blog_articles_1_xiao_wang.xlsx',
 'crazyant_blog_articles_2_xiao_ming.xlsx',
 'crazyant_blog_articles_3_xiao_lei.xlsx',
 'crazyant_blog_articles_4_xiao_bo.xlsx',
 'crazyant_blog_articles_5_xiao_hong.xlsx']

2. 分别读取到dataframe

df_list = []

for excel_name in excel_names:
    # 读取每个excel到df
    excel_path = f"{splits_dir}/{excel_name}"
    df_split = pd.read_excel(excel_path)
    # 得到username
    username = excel_name.replace("crazyant_blog_articles_", "").replace(".xlsx", "")[2:]
    print(excel_name, username)
    # 给每个df添加1列,即用户名字
    df_split["username"] = username

    df_list.append(df_split)
crazyant_blog_articles_0_xiao_shuai.xlsx xiao_shuai
crazyant_blog_articles_1_xiao_wang.xlsx xiao_wang
crazyant_blog_articles_2_xiao_ming.xlsx xiao_ming
crazyant_blog_articles_3_xiao_lei.xlsx xiao_lei
crazyant_blog_articles_4_xiao_bo.xlsx xiao_bo
crazyant_blog_articles_5_xiao_hong.xlsx xiao_hong

3. 使用pd.concat进行合并

df_merged = pd.concat(df_list)
df_merged.shape
(258, 4)
df_merged.head()
idtitletagsusername
02585Tensorflow怎样接收变长列表特征python,tensorflow,特征工程xiao_shuai
12583Pandas实现数据的合并concatpandas,python,数据分析xiao_shuai
22574Pandas的Index索引有什么用途?pandas,python,数据分析xiao_shuai
32564机器学习常用数据集大全python,机器学习xiao_shuai
42561一个数据科学家的修炼路径数据分析xiao_shuai
df_merged["username"].value_counts()
xiao_hong     43
xiao_bo       43
xiao_shuai    43
xiao_lei      43
xiao_wang     43
xiao_ming     43
Name: username, dtype: int64

4. 将合并后的dataframe输出到excel

df_merged.to_excel(f"{work_dir}/crazyant_blog_articles_merged.xlsx", index=False)

Pandas实现数据的合并concat

使用场景:

批量合并相同格式的Excel、给DataFrame添加行、给DataFrame添加列

一句话说明concat语法:

  • 使用某种合并方式(inner/outer)
  • 沿着某个轴向(axis=0/1)
  • 把多个Pandas对象(DataFrame/Series)合并成一个。

concat语法:pandas.concat(objs, axis=0, join=’outer’, ignore_index=False)

  • objs:一个列表,内容可以是DataFrame或者Series,可以混合
  • axis:默认是0代表按行合并,如果等于1代表按列合并
  • join:合并的时候索引的对齐方式,默认是outer join,也可以是inner join
  • ignore_index:是否忽略掉原来的数据索引

append语法:DataFrame.append(other, ignore_index=False)

append只有按行合并,没有按列合并,相当于concat按行的简写形式
* other:单个dataframe、series、dict,或者列表
* ignore_index:是否忽略掉原来的数据索引

参考文档:

  • pandas.concat的api文档:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.concat.html
  • pandas.concat的教程:https://pandas.pydata.org/pandas-docs/stable/user_guide/merging.html
  • pandas.append的api文档:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.append.html
import pandas as pd

import warnings
warnings.filterwarnings('ignore')

一、使用pandas.concat合并数据

df1 = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],
                    'B': ['B0', 'B1', 'B2', 'B3'],
                    'C': ['C0', 'C1', 'C2', 'C3'],
                    'D': ['D0', 'D1', 'D2', 'D3'],
                    'E': ['E0', 'E1', 'E2', 'E3']
                   })
df1
ABCDE
0A0B0C0D0E0
1A1B1C1D1E1
2A2B2C2D2E2
3A3B3C3D3E3
df2 = pd.DataFrame({ 'A': ['A4', 'A5', 'A6', 'A7'],
                     'B': ['B4', 'B5', 'B6', 'B7'],
                     'C': ['C4', 'C5', 'C6', 'C7'],
                     'D': ['D4', 'D5', 'D6', 'D7'],
                     'F': ['F4', 'F5', 'F6', 'F7']
                   })
df2
ABCDF
0A4B4C4D4F4
1A5B5C5D5F5
2A6B6C6D6F6
3A7B7C7D7F7

1、默认的concat,参数为axis=0、join=outer、ignore_index=False

pd.concat([df1,df2])
ABCDEF
0A0B0C0D0E0NaN
1A1B1C1D1E1NaN
2A2B2C2D2E2NaN
3A3B3C3D3E3NaN
0A4B4C4D4NaNF4
1A5B5C5D5NaNF5
2A6B6C6D6NaNF6
3A7B7C7D7NaNF7

2、使用ignore_index=True可以忽略原来的索引

pd.concat([df1,df2], ignore_index=True)
ABCDEF
0A0B0C0D0E0NaN
1A1B1C1D1E1NaN
2A2B2C2D2E2NaN
3A3B3C3D3E3NaN
4A4B4C4D4NaNF4
5A5B5C5D5NaNF5
6A6B6C6D6NaNF6
7A7B7C7D7NaNF7

3、使用join=inner过滤掉不匹配的列

pd.concat([df1,df2], ignore_index=True, join="inner")
ABCD
0A0B0C0D0
1A1B1C1D1
2A2B2C2D2
3A3B3C3D3
4A4B4C4D4
5A5B5C5D5
6A6B6C6D6
7A7B7C7D7

4、使用axis=1相当于添加新列

df1
ABCDE
0A0B0C0D0E0
1A1B1C1D1E1
2A2B2C2D2E2
3A3B3C3D3E3

A:添加一列Series

s1 = pd.Series(list(range(4)), name="F")
pd.concat([df1,s1], axis=1)
ABCDEF
0A0B0C0D0E00
1A1B1C1D1E11
2A2B2C2D2E22
3A3B3C3D3E33

B:添加多列Series

s2 = df1.apply(lambda x:x["A"]+"_GG", axis=1)
s2
0    A0_GG
1    A1_GG
2    A2_GG
3    A3_GG
dtype: object
s2.name="G"
pd.concat([df1,s1,s2], axis=1)
ABCDEFG
0A0B0C0D0E00A0_GG
1A1B1C1D1E11A1_GG
2A2B2C2D2E22A2_GG
3A3B3C3D3E33A3_GG
# 列表可以只有Series
pd.concat([s1,s2], axis=1)
FG
00A0_GG
11A1_GG
22A2_GG
33A3_GG
# 列表是可以混合顺序的
pd.concat([s1,df1,s2], axis=1)
FABCDEG
00A0B0C0D0E0A0_GG
11A1B1C1D1E1A1_GG
22A2B2C2D2E2A2_GG
33A3B3C3D3E3A3_GG

二、使用DataFrame.append按行合并数据

df1 = pd.DataFrame([[1, 2], [3, 4]], columns=list('AB'))
df1
AB
012
134
df2 = pd.DataFrame([[5, 6], [7, 8]], columns=list('AB'))
df2
AB
056
178

1、给1个dataframe添加另一个dataframe

df1.append(df2)
AB
012
134
056
178

2、忽略原来的索引ignore_index=True

df1.append(df2, ignore_index=True)
AB
012
134
256
378

3、可以一行一行的给DataFrame添加数据

# 一个空的df
df = pd.DataFrame(columns=['A'])
df
A

A:低性能版本

for i in range(5):
    # 注意这里每次都在复制
    df = df.append({'A': i}, ignore_index=True)
df
A
00
11
22
33
44

B:性能好的版本

# 第一个入参是一个列表,避免了多次复制
pd.concat(
    [pd.DataFrame([i], columns=['A']) for i in range(5)],
    ignore_index=True
)
A
00
11
22
33
44

Pandas的Index索引有什么用途?

把数据存储于普通的column列也能用于数据查询,那使用index有什么好处?

index的用途总结:
1. 更方便的数据查询;
2. 使用index可以获得性能提升;
3. 自动的数据对齐功能;
4. 更多更强大的数据结构支持;

import pandas as pd
df = pd.read_csv("./datas/ml-latest-small/ratings.csv")
df.head()
userIdmovieIdratingtimestamp
0114.0964982703
1134.0964981247
2164.0964982224
31475.0964983815
41505.0964982931
df.count()
userId       100836
movieId      100836
rating       100836
timestamp    100836
dtype: int64

1、使用index查询数据

# drop==False,让索引列还保持在column
df.set_index("userId", inplace=True, drop=False)
df.head()
userIdmovieIdratingtimestamp
userId
1114.0964982703
1134.0964981247
1164.0964982224
11475.0964983815
11505.0964982931
df.index
Int64Index([  1,   1,   1,   1,   1,   1,   1,   1,   1,   1,
            ...
            610, 610, 610, 610, 610, 610, 610, 610, 610, 610],
           dtype='int64', name='userId', length=100836)
# 使用index的查询方法
df.loc[500].head(5)
userIdmovieIdratingtimestamp
userId
50050014.01005527755
500500111.01005528017
500500391.01005527926
5005001011.01005527980
5005001044.01005528065
# 使用column的condition查询方法
df.loc[df["userId"] == 500].head()
userIdmovieIdratingtimestamp
userId
50050014.01005527755
500500111.01005528017
500500391.01005527926
5005001011.01005527980
5005001044.01005528065

2. 使用index会提升查询性能

  • 如果index是唯一的,Pandas会使用哈希表优化,查询性能为O(1);
  • 如果index不是唯一的,但是有序,Pandas会使用二分查找算法,查询性能为O(logN);
  • 如果index是完全随机的,那么每次查询都要扫描全表,查询性能为O(N);

实验1:完全随机的顺序查询

# 将数据随机打散
from sklearn.utils import shuffle
df_shuffle = shuffle(df)
df_shuffle.head()
userIdmovieIdratingtimestamp
userId
16016023401.0985383314
12912911363.51167375403
167167441914.51154718915
5365362763.0832839990
676759522.01501274082
# 索引是否是递增的
df_shuffle.index.is_monotonic_increasing
False
df_shuffle.index.is_unique
False
# 计时,查询id==500数据性能
%timeit df_shuffle.loc[500]
376 µs ± 52.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

实验2:将index排序后的查询

df_sorted = df_shuffle.sort_index()
df_sorted.head()
userIdmovieIdratingtimestamp
userId
1129854.0964983034
1126172.0964982588
1136394.0964982271
1164.0964982224
117334.0964982400
# 索引是否是递增的
df_sorted.index.is_monotonic_increasing
True
df_sorted.index.is_unique
False
%timeit df_sorted.loc[500]
203 µs ± 20.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

3. 使用index能自动对齐数据

包括series和dataframe

s1 = pd.Series([1,2,3], index=list("abc"))
s1
a    1
b    2
c    3
dtype: int64
s2 = pd.Series([2,3,4], index=list("bcd"))
s2
b    2
c    3
d    4
dtype: int64
s1+s2
a    NaN
b    4.0
c    6.0
d    NaN
dtype: float64

4. 使用index更多更强大的数据结构支持

很多强大的索引数据结构
* CategoricalIndex,基于分类数据的Index,提升性能;
* MultiIndex,多维索引,用于groupby多维聚合后结果等;
* DatetimeIndex,时间类型索引,强大的日期和时间的方法支持;

Pandas的axis参数怎么理解?

axis参数非常的让人困惑难以理解,本视频我会用形象化的方式讲解一下这个参数,核心要诀就是axis那个轴会消失!

  • axis=0或者”index”:
    • 如果是单行操作,就指的是某一行
    • 如果是聚合操作,指的是跨行cross rows
  • axis=1或者”columns”:
    • 如果是单列操作,就指的是某一列
    • 如果是聚合操作,指的是跨列cross columns

按哪个axis,就是这个axis要动起来(类似被for遍历),其它的axis保持不动

import pandas as pd
import numpy as np
df = pd.DataFrame(
    np.arange(12).reshape(3,4),
    columns=['A', 'B', 'C', 'D']
)
df
ABCD
00123
14567
2891011

1、单列drop,就是删除某一列

# 代表的就是删除某列
df.drop("A", axis=1)
BCD
0123
1567
291011

2、单行drop,就是删除某一行

df
ABCD
00123
14567
2891011
# 代表的就是删除某行
df.drop(1, axis=0)
ABCD
00123
2891011

3、按axis=0/index执行mean聚合操作

反直觉:输出的不是每行的结果,而是每列的结果

df
ABCD
00123
14567
2891011
# axis=0 or axis=index
df.mean(axis=0)
A    4.0
B    5.0
C    6.0
D    7.0
dtype: float64

指定了按哪个axis,就是这个axis要动起来(类似被for遍历),其它的axis保持不动

4、按axis=1/columns执行mean聚合操作

反直觉:输出的不是每行的结果,而是每列的结果

df
ABCD
00123
14567
2891011
# axis=1 or axis=columns
df.mean(axis=1)
0    1.5
1    5.5
2    9.5
dtype: float64

指定了按哪个axis,就是这个axis要动起来(类似被for遍历),其它的axis保持不动

5、再次举例,加深理解

def get_sum_value(x):
    return x["A"] + x["B"] + x["C"] + x["D"]

df["sum_value"] = df.apply(get_sum_value, axis=1)
df
ABCDsum_value
001236
1456722
289101138

指定了按哪个axis,就是这个axis要动起来(类似被for遍历),其它的axis保持不动

本文地址:http://www.crazyant.net/2546.html
转载请注明来源

Pandas怎样处理字符串?

前面我们已经使用了字符串的处理函数:
df[“bWendu”].str.replace(“℃”, “”).astype(‘int32’)

Pandas的字符串处理:
1. 使用方法:先获取Series的str属性,然后在属性上调用函数;
2. 只能在字符串列上使用,不能数字列上使用;
3. Dataframe上没有str属性和处理方法
4. Series.str并不是Python原生字符串,而是自己的一套方法,不过大部分和原生str很相似;

Series.str字符串方法列表参考文档:
https://pandas.pydata.org/pandas-docs/stable/reference/series.html#string-handling

本节演示内容:
1. 获取Series的str属性,然后使用各种字符串处理函数
2. 使用str的startswith、contains等bool类Series可以做条件查询
3. 需要多次str处理的链式操作
4. 使用正则表达式的处理

0、读取北京2018年天气数据

import pandas as pd
fpath = "./datas/beijing_tianqi/beijing_tianqi_2018.csv"
df = pd.read_csv(fpath)
df.head()
ymdbWenduyWendutianqifengxiangfengliaqiaqiInfoaqiLevel
02018-01-013℃-6℃晴~多云东北风1-2级592
12018-01-022℃-5℃阴~多云东北风1-2级491
22018-01-032℃-5℃多云北风1-2级281
32018-01-040℃-8℃东北风1-2级281
42018-01-053℃-6℃多云~晴西北风1-2级501
df.dtypes
ymd          object
bWendu       object
yWendu       object
tianqi       object
fengxiang    object
fengli       object
aqi           int64
aqiInfo      object
aqiLevel      int64
dtype: object

1、获取Series的str属性,使用各种字符串处理函数

df["bWendu"].str
<pandas.core.strings.StringMethods at 0x1af21871808>
# 字符串替换函数
df["bWendu"].str.replace("℃", "")
0       3
1       2
2       2
3       0
4       3
       ..
360    -5
361    -3
362    -3
363    -2
364    -2
Name: bWendu, Length: 365, dtype: object
# 判断是不是数字
df["bWendu"].str.isnumeric()
0      False
1      False
2      False
3      False
4      False
       ...  
360    False
361    False
362    False
363    False
364    False
Name: bWendu, Length: 365, dtype: bool
df["aqi"].str.len()
---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-8-12cdcbdb6f81> in <module>
----> 1 df["aqi"].str.len()


d:\appdata\python37\lib\site-packages\pandas\core\generic.py in __getattr__(self, name)
   5173             or name in self._accessors
   5174         ):
-> 5175             return object.__getattribute__(self, name)
   5176         else:
   5177             if self._info_axis._can_hold_identifiers_and_holds_name(name):


d:\appdata\python37\lib\site-packages\pandas\core\accessor.py in __get__(self, obj, cls)
    173             # we're accessing the attribute of the class, i.e., Dataset.geo
    174             return self._accessor
--> 175         accessor_obj = self._accessor(obj)
    176         # Replace the property with the accessor object. Inspired by:
    177         # http://www.pydanny.com/cached-property.html


d:\appdata\python37\lib\site-packages\pandas\core\strings.py in __init__(self, data)
   1915 
   1916     def __init__(self, data):
-> 1917         self._inferred_dtype = self._validate(data)
   1918         self._is_categorical = is_categorical_dtype(data)
   1919 


d:\appdata\python37\lib\site-packages\pandas\core\strings.py in _validate(data)
   1965 
   1966         if inferred_dtype not in allowed_types:
-> 1967             raise AttributeError("Can only use .str accessor with string " "values!")
   1968         return inferred_dtype
   1969 


AttributeError: Can only use .str accessor with string values!

2、使用str的startswith、contains等得到bool的Series可以做条件查询

condition = df["ymd"].str.startswith("2018-03")
condition
0      False
1      False
2      False
3      False
4      False
       ...  
360    False
361    False
362    False
363    False
364    False
Name: ymd, Length: 365, dtype: bool
df[condition].head()
ymdbWenduyWendutianqifengxiangfengliaqiaqiInfoaqiLevel
592018-03-018℃-3℃多云西南风1-2级461
602018-03-029℃-1℃晴~多云北风1-2级952
612018-03-0313℃3℃多云~阴北风1-2级214重度污染5
622018-03-047℃-2℃阴~多云东南风1-2级144轻度污染3
632018-03-058℃-3℃南风1-2级942

3、需要多次str处理的链式操作

怎样提取201803这样的数字月份?
1、先将日期2018-03-31替换成20180331的形式
2、提取月份字符串201803

df["ymd"].str.replace("-", "")
0      20180101
1      20180102
2      20180103
3      20180104
4      20180105
         ...   
360    20181227
361    20181228
362    20181229
363    20181230
364    20181231
Name: ymd, Length: 365, dtype: object
# 每次调用函数,都返回一个新Series
df["ymd"].str.replace("-", "").slice(0, 6)
---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-13-ae278fb12255> in <module>
      1 # 每次调用函数,都返回一个新Series
----> 2 df["ymd"].str.replace("-", "").slice(0, 6)


d:\appdata\python37\lib\site-packages\pandas\core\generic.py in __getattr__(self, name)
   5177             if self._info_axis._can_hold_identifiers_and_holds_name(name):
   5178                 return self[name]
-> 5179             return object.__getattribute__(self, name)
   5180 
   5181     def __setattr__(self, name, value):


AttributeError: 'Series' object has no attribute 'slice'
df["ymd"].str.replace("-", "").str.slice(0, 6)
0      201801
1      201801
2      201801
3      201801
4      201801
        ...  
360    201812
361    201812
362    201812
363    201812
364    201812
Name: ymd, Length: 365, dtype: object
# slice就是切片语法,可以直接用
df["ymd"].str.replace("-", "").str[0:6]
0      201801
1      201801
2      201801
3      201801
4      201801
        ...  
360    201812
361    201812
362    201812
363    201812
364    201812
Name: ymd, Length: 365, dtype: object

4. 使用正则表达式的处理

# 添加新列
def get_nianyueri(x):
    year,month,day = x["ymd"].split("-")
    return f"{year}年{month}月{day}日"
df["中文日期"] = df.apply(get_nianyueri, axis=1)
df["中文日期"]
0      2018年01月01日
1      2018年01月02日
2      2018年01月03日
3      2018年01月04日
4      2018年01月05日
          ...     
360    2018年12月27日
361    2018年12月28日
362    2018年12月29日
363    2018年12月30日
364    2018年12月31日
Name: 中文日期, Length: 365, dtype: object

问题:怎样将“2018年12月31日”中的年、月、日三个中文字符去除?

# 方法1:链式replace
df["中文日期"].str.replace("年", "").str.replace("月","").str.replace("日", "")
0      20180101
1      20180102
2      20180103
3      20180104
4      20180105
         ...   
360    20181227
361    20181228
362    20181229
363    20181230
364    20181231
Name: 中文日期, Length: 365, dtype: object

Series.str默认就开启了正则表达式模式

# 方法2:正则表达式替换
df["中文日期"].str.replace("[年月日]", "")
0      20180101
1      20180102
2      20180103
3      20180104
4      20180105
         ...   
360    20181227
361    20181228
362    20181229
363    20181230
364    20181231
Name: 中文日期, Length: 365, dtype: object

Pandas怎样对数据进行排序?

Series的排序:
Series.sort_values(ascending=True, inplace=False)
参数说明:
* ascending:默认为True升序排序,为False降序排序
* inplace:是否修改原始Series

DataFrame的排序:
DataFrame.sort_values(by, ascending=True, inplace=False)
参数说明:
* by:字符串或者List<字符串>,单列排序或者多列排序
* ascending:bool或者List,升序还是降序,如果是list对应by的多列
* inplace:是否修改原始DataFrame

import pandas as pd

0、读取数据

fpath = "./datas/beijing_tianqi/beijing_tianqi_2018.csv"
df = pd.read_csv(fpath)

# 替换掉温度的后缀℃
df.loc[:, "bWendu"] = df["bWendu"].str.replace("℃", "").astype('int32')
df.loc[:, "yWendu"] = df["yWendu"].str.replace("℃", "").astype('int32')
df.head()
ymdbWenduyWendutianqifengxiangfengliaqiaqiInfoaqiLevel
02018-01-013-6晴~多云东北风1-2级592
12018-01-022-5阴~多云东北风1-2级491
22018-01-032-5多云北风1-2级281
32018-01-040-8东北风1-2级281
42018-01-053-6多云~晴西北风1-2级501

1、Series的排序

df["aqi"].sort_values()
271     21
281     21
249     22
272     22
301     22
      ... 
317    266
71     287
91     287
72     293
86     387
Name: aqi, Length: 365, dtype: int64
df["aqi"].sort_values(ascending=False)
86     387
72     293
91     287
71     287
317    266
      ... 
301     22
272     22
249     22
281     21
271     21
Name: aqi, Length: 365, dtype: int64
df["tianqi"].sort_values()
225     中雨~小雨
230     中雨~小雨
197    中雨~雷阵雨
196    中雨~雷阵雨
112        多云
        ...  
191    雷阵雨~大雨
219     雷阵雨~阴
335      雾~多云
353         霾
348         霾
Name: tianqi, Length: 365, dtype: object

2、DataFrame的排序

2.1 单列排序

df.sort_values(by="aqi")
ymdbWenduyWendutianqifengxiangfengliaqiaqiInfoaqiLevel
2712018-09-292211北风3-4级211
2812018-10-09154多云~晴西北风4-5级211
2492018-09-072716西北风3-4级221
2722018-09-301913多云西北风4-5级221
3012018-10-29153北风3-4级221
3172018-11-14135多云南风1-2级266重度污染5
712018-03-13175晴~多云南风1-2级287重度污染5
912018-04-022611多云北风1-2级287重度污染5
722018-03-14156多云~阴东北风1-2级293重度污染5
862018-03-28259多云~晴东风1-2级387严重污染6

365 rows × 9 columns

df.sort_values(by="aqi", ascending=False)
ymdbWenduyWendutianqifengxiangfengliaqiaqiInfoaqiLevel
862018-03-28259多云~晴东风1-2级387严重污染6
722018-03-14156多云~阴东北风1-2级293重度污染5
712018-03-13175晴~多云南风1-2级287重度污染5
912018-04-022611多云北风1-2级287重度污染5
3172018-11-14135多云南风1-2级266重度污染5
2492018-09-072716西北风3-4级221
3012018-10-29153北风3-4级221
2722018-09-301913多云西北风4-5级221
2712018-09-292211北风3-4级211
2812018-10-09154多云~晴西北风4-5级211

365 rows × 9 columns

2.2 多列排序

# 按空气质量等级、最高温度排序,默认升序
df.sort_values(by=["aqiLevel", "bWendu"])
ymdbWenduyWendutianqifengxiangfengliaqiaqiInfoaqiLevel
3602018-12-27-5-12多云~晴西北风3级481
222018-01-23-4-12西北风3-4级311
232018-01-24-4-11西南风1-2级341
3402018-12-07-4-10西北风3级331
212018-01-22-3-10小雪~多云东风1-2级471
712018-03-13175晴~多云南风1-2级287重度污染5
902018-04-012511晴~多云南风1-2级218重度污染5
912018-04-022611多云北风1-2级287重度污染5
852018-03-272711南风1-2级243重度污染5
862018-03-28259多云~晴东风1-2级387严重污染6

365 rows × 9 columns

# 两个字段都是降序
df.sort_values(by=["aqiLevel", "bWendu"], ascending=False)
ymdbWenduyWendutianqifengxiangfengliaqiaqiInfoaqiLevel
862018-03-28259多云~晴东风1-2级387严重污染6
852018-03-272711南风1-2级243重度污染5
912018-04-022611多云北风1-2级287重度污染5
902018-04-012511晴~多云南风1-2级218重度污染5
712018-03-13175晴~多云南风1-2级287重度污染5
3622018-12-29-3-12西北风2级291
222018-01-23-4-12西北风3-4级311
232018-01-24-4-11西南风1-2级341
3402018-12-07-4-10西北风3级331
3602018-12-27-5-12多云~晴西北风3级481

365 rows × 9 columns

# 分别指定升序和降序
df.sort_values(by=["aqiLevel", "bWendu"], ascending=[True, False])
ymdbWenduyWendutianqifengxiangfengliaqiaqiInfoaqiLevel
1782018-06-283524多云~晴北风1-2级331
1492018-05-303318西风1-2级461
2062018-07-263325多云~雷阵雨东北风1-2级401
1582018-06-083219多云~雷阵雨西南风1-2级431
2052018-07-253225多云北风1-2级281
3172018-11-14135多云南风1-2级266重度污染5
3292018-11-26100多云东南风1级245重度污染5
3352018-12-0292雾~多云东北风1级234重度污染5
572018-02-2770东风1-2级220重度污染5
862018-03-28259多云~晴东风1-2级387严重污染6

365 rows × 9 columns

本文章有配套视频讲解,关注公众号:蚂蚁学Python,查看视频、获取源码