前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Pandas从小白到大师

Pandas从小白到大师

作者头像
MeteoAI
发布2019-08-06 17:04:45
1K0
发布2019-08-06 17:04:45
举报
文章被收录于专栏:MeteoAIMeteoAI

说明:本文内容翻译、节选自外文From Pandas-wan to Pandas-master[1],原作者Rudolf Höhn小哥,实验数据来自kaggle [2]的各国自杀率预测竞赛,都需要访问外国网站,相关文件已上传到本人的github中[3][4]。

本文主要包含如下部分:

1.数据探索2.内存优化3.索引4.方法串联(method chaining)

数据探索

代码语言:javascript
复制
import pandas as pd
import numpy as np
import os
data_path = 'path/to/folder/' # 注意修改路径
df = (pd.read_csv(filepath_or_buffer=os.path.join(data_path, 'master.csv'))
      .rename(columns={'suicides/100k pop' : 'suicides_per_100k',
                       ' gdp_for_year ($) ' : 'gdp_year', 
                       'gdp_per_capita ($)' : 'gdp_capita',
                       'country-year' : 'country_year'})
      .assign(gdp_year=lambda _df: _df['gdp_year'].str.replace(',','').astype(np.int64))
     )

代码语言:javascript
复制
df.describe(include='all')

代码语言:javascript
复制
df.dtypes

代码语言:javascript
复制
country               object
year                   int64
sex                   object
age                   object
suicides_no            int64
population             int64
suicides_per_100k    float64
country_year          object
HDI for year         float64
gdp_year               int64
gdp_capita             int64
generation            object
dtype: object

内存优化

在处理数据之前,一个重要的步骤是理解数据并为各列数据选择合适的数据类型,这里有两种方法可以显著地降低你的内存消耗。

代码语言:javascript
复制
import pandas as pd
def mem_usage(df: pd.DataFrame) -> str:
    """This method styles the memory usage of a DataFrame to be readable as MB.
    Parameters
    ----------
    df: pd.DataFrame
        Data frame to measure.
    Returns
    -------
    str
        Complete memory usage as a string formatted for MB.
    """
    return f'{df.memory_usage(deep=True).sum() / 1024 ** 2 : 3.2f} MB'
def convert_df(df: pd.DataFrame, deep_copy: bool = True) -> pd.DataFrame:
    """Automatically converts columns that are worth stored as
    ``categorical`` dtype.
    Parameters
    ----------
    df: pd.DataFrame
        Data frame to convert.
    deep_copy: bool
        Whether or not to perform a deep copy of the original data frame.
    Returns
    -------
    pd.DataFrame
        Optimized copy of the input data frame.
    """
    return df.copy(deep=deep_copy).astype({
        col: 'category' for col in df.columns
        if df[col].nunique() / df[col].shape[0] < 0.5})

pandas 提供了 memory_usage()方法来分析数据的内存消耗,在代码中,deep = True 确保真正使用了系统内存。理解列的类型非常重要,这可以节省你90%以上的内存。比如对与price这一列来讲,float64浮点类型可能会产生不必要的消耗,所以要尽量使用int32型。

回到我们定义的convert_df()方法上来,如果某一列百分之50以上的值都是独一无二的(unique),它可以自动地把列的类型转换为类别变量。

让我们看看数据都发生了什么神奇变化吧!

代码语言:javascript
复制
>>> mem_usage(df)
10.28 MB
>>> mem_usage(df.set_index(['country', 'year', 'sex', 'age']))
5.00 MB
>>> mem_usage(convert_df(df))
1.40 MB
>>> mem_usage(convert_df(df.set_index(['country', 'year', 'sex', 'age'])))
1.40 MB

通过变换,datafram数据的内存消耗只有原来的十分之一了!!!

索引

在pandas中,我们有两种方式获得数据,一种是通过索引(indexing),另外一种是通过查询(query),在大多数情况下,通过索引(或者多重索引)效果更佳,让我们看一下例子吧!

代码语言:javascript
复制
>>> %%time
>>> df.query('country == "Albania" and year == 1987 and sex == "male" and age == "25-34 years"')  # 查询
CPU times: user 7.27 ms, sys: 751 µs, total: 8.02 ms
# =========下面使用多重索引=========
%%time
mi_df = df.set_index(['country', 'year', 'sex', 'age']) # 创建多重索引的时间
CPU times: user 10.8 ms, sys: 2.2 ms, total: 13 ms
>>> %%time  
>>> mi_df.loc['Albania', 1987, 'male', '25-34 years'] #
CPU times: user 459 µs, sys: 1 µs, total: 460 µs
#===================

query方式总共消耗了7.27ms , 索引方式总的消耗时间为(创建多重索引的时间10秒)+(查询的时间459us)

所以,如果你只使用一次数据(当然这种情况很少见),请使用query查询方式, 否则使用索引方式,因为一旦我们有了多重索引,通过索引获取数据相当高效。

方法串联(method chaining)

‘’方法串联‘’ 指把 一系列的多个方法(method)串联起来,最后返回dataframe,这样可以避免中间变量的产生,从而节省内存。

笨方法

代码语言:javascript
复制
import numpy as np
import pandas as pd
df = pd.DataFrame({'a_column': [1, -999, -999],
                    'powerless_column': [2, 3, 4],
                    'int_column': [1, 1, -1]})
df['a_column'] = df['a_column'].replace(-999, np.nan)
df['power_column'] = df['powerless_column'] ** 2
df['real_column'] = df['int_column'].astype(np.float64)
df = df.apply(lambda _df: _df.replace(4, np.nan))
df = df.dropna(how='all')

避免产生中间变量的方法

代码语言:javascript
复制
df = (pd.DataFrame({'a_column': [1, -999, -999],
                    'powerless_column': [2, 3, 4],
                    'int_column': [1, 1, -1]})
        .assign(a_column=lambda _df: _df['a_column'].replace(-999, np.nan),
                power_column=lambda _df: _df['powerless_column'] ** 2,
                real_column=lambda _df: _df['int_column'].astype(np.float64))
        .apply(lambda _df: _df.replace(4, np.nan))
        .dropna(how='all')
      )

对我们的自杀数据进行处理:

代码语言:javascript
复制
(df
 .groupby('age')
 .agg({'generation':'unique'})
 .rename(columns={'generation':'unique_generation'})
#  Recommended from v0.25
#  .agg(unique_generation=('generation', 'unique'))
)

age

unique_generation

15-24 years

[Generation X, Millenials]

25-34 years

[Boomers, Generation X, Millenials]

35-54 years

[Silent, Boomers, Generation X]

5-14 years

[Generation X, Millenials, Generation Z]

55-74 years

[G.I. Generation, Silent, Boomers]

75+ years

[G.I. Generation, Silent]

上述的代码先是对df进行年龄分组,返回一个dataFrameGroupBy的类型数据,之后再个各个组进行聚合操作(agg),得到每组独一无二的值。

该方法也可以接受任意函数(functions),在0.25版本的pandas中,新增了新的使用agg的方式:

代码语言:javascript
复制
#使用sort_values函数和head 函数 排序并得到前10名
(df
 .groupby(['country', 'year'])
 .agg({'suicides_per_100k': 'sum'})
 .rename(columns={'suicides_per_100k':'suicides_sum'})
#  Recommended from v0.25
#  .agg(suicides_sum=('suicides_per_100k', 'sum'))
 .sort_values('suicides_sum', ascending=False)
 .head(10)

country

year

suices_sum

Lithuania

1995

639.30

1996

595.61

580

Hungary

1991

575.00

Lithuania

2000

571.80

Hungary

1992

570.26

Lithuania

2001

568.98

Russian Federation

1994

567.64

Lithuania

1998

566.36

1997

565.44

577

1999

561.53

899

代码语言:javascript
复制
#直接使用nlargest 函数得到新列suicides_sum的前10名
(df
 .groupby(['country', 'year'])
 .agg({'suicides_per_100k': 'sum'})
 .rename(columns={'suicides_per_100k':'suicides_sum'})
#  Recommended from v0.25
#  .agg(suicides_sum=('suicides_per_100k', 'sum'))
 .nlargest(10, columns='suicides_sum')
)

country

year

suicides_sum

Lithuania

1995

639.30

1996

595.61

435

Hungary

1991

575.00

Lithuania

2000

571.80

Hungary

1992

570.26

Lithuania

2001

568.98

Russian Federation

1994

567.64

Lithuania

1998

566.36

1997

565.44

567

1999

561.53

434

两种方法,使用nlargest更加简洁些。

另外一个有趣的方法是unstack,其允许反转坐标轴。

代码语言:javascript
复制
mi_df.loc[('Switzerland', 2000)]

可以看到,上面数据的行索引是性别、年龄,将性别展开(unstack)后,选择自杀数 和人口数这两列,得到如下,以前性别作为行索引,现在性别变成了列

代码语言:javascript
复制
(mi_df
 .loc[('Switzerland', 2000)]
 .unstack('sex')
 [['suicides_no', 'population']]
)

如果我们不取自杀数和人口数这两列,只unstack('sex'):

另外一种方法是使用pipe进行串联操作,一个简单有效的例子是查询数据的不同信息:

代码语言:javascript
复制
def log_head(df, head_count=10):
    print(df.head(head_count))
    return df

def log_columns(df):
    print(df.columns)
    return df

def log_shape(df):
    print(f'shape = {df.shape}')
    return df

(df
 .pipe(log_shape)
 .query('sex == "female"')
 .groupby(['year', 'country'])
 .agg({'suicides_per_100k':'sum'})
 .pipe(log_shape)
 .rename(columns={'suicides_per_100k':'sum_suicides_per_100k_female'})
#  Recommended from v0.25
#  .agg(sum_suicides_per_100k_female=('suicides_per_100k', 'sum'))
 .nlargest(n=10, columns=['sum_suicides_per_100k_female'])
)

代码语言:javascript
复制
shape = (27820, 12)
shape = (2321, 1)

year

country

sum_suicedes_per_100k_femal

2009

Republic of Korea

170.89

1989

Singapore

163.16

1986

Singapore

161.67

2010

Republic of Korea

158.52

2007

Republic of Korea

149.60

2011

Republic of Korea

147.84

1991

Hungary

147.35

2008

Republic of Korea

147.04

2000

Aruba

146.22

2005

Republic of Korea

145.35

References

[1] : https://medium.com/unit8-machine-learning-publication/from-pandas-wan-to-pandas-master-4860cf0ce442 [2] : https://www.kaggle.com/russellyates88/suicide-rates-overview-1985-to-2016/downloads/suicide-rates-overview-1985-to-2016.zip/1 [3] : https://github.com/deepwindlee/MySQL-with-Python-DATA-MINING/blob/master/from_pandas-wan_to_pandas-master.ipynb [4] : https://github.com/deepwindlee/MySQL-with-Python-DATA-MINING/blob/master/suicide.csv

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-08-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 MeteoAI 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 数据探索
  • 内存优化
  • 索引
  • 方法串联(method chaining)
    • 笨方法
      • 避免产生中间变量的方法
        • References
        相关产品与服务
        云数据库 MySQL
        腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档