专栏名称: 爱数据LoveData
中国统计网(www.itongji.cn),国内最大的数据分析门户网站。提供数据分析行业资讯,统计百科知识、数据分析、商业智能(BI)、数据挖掘技术,Excel、SPSS、SAS、R等数据分析软件等在线学习平台。
目录
相关文章推荐
51好读  ›  专栏  ›  爱数据LoveData

从小白到大师,这里有一份Pandas入门指南

爱数据LoveData  · 公众号  · BI  · 2019-10-16 14:00

正文

请到「今天看啥」查看全文


return f' {df.memory_usage(deep= True ).sum() / 1024 ** 2 : 3.2 f} 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 来确保考虑到了实际的系统使用情况。


memory_usage():https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.memory_usage.html


了解列的类型(https://pandas.pydata.org/pandas-docs/stable/getting_started/basics.html#basics-dtypes)很重要。它可以通过两种简单的方法节省高达 90% 的内存使用:


  • 了解数据框使用的类型;

  • 了解数据框可以使用哪种类型来减少内存的使用(例如,price 这一列值在 0 到 59 之间,只带有一位小数,使用 float64 类型可能会产生不必要的内存开销)


除了降低数值类型的大小(用 int32 而不是 int64)外,Pandas 还提出了分类类型: https://pandas.pydata.org/pandas-docs/stable/user_guide/categorical.html
如果你是用 R 语言的开发人员,你可能觉得它和 factor 类型是一样的。


这种分类类型允许用索引替换重复值,还可以把实际值存在其他位置。教科书中的例子是国家。和多次存储相同的字符串「瑞士」或「波兰」比起来,为什么不简单地用 0 和 1 替换它们,并存储在字典中呢?


categorical_dict = {0: 'Switzerland', 1: 'Poland'}

Pandas 做了几乎相同的工作,同时添加了所有的方法,可以实际使用这种类型,并且仍然能够显示国家的名称。


回到 convert_df() 方法,如果这一列中的唯一值小于 50%,它会自动将列类型转换成 category。这个数是任意的,但是因为数据框中类型的转换意味着在 numpy 数组间移动数据,因此我们得到的必须比失去的多。


接下来看看数据中会发生什么。


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

通过使用「智能」转换器,数据框使用的内存几乎减少了 10 倍(准确地说是 7.34 倍)。


索引


Pandas 是强大的,但也需要付出一些代价。当你加载 DataFrame 时,它会创建索引并将数据存储在 numpy 数组中。这是什么意思?一旦加载了数据框,只要正确管理索引,就可以快速地访问数据。


访问数据的方法主要有两种,分别是通过索引和查询访问。根据具体情况,你只能选择其中一种。但在大多数情况中,索引(和多索引)都是最好的选择。我们来看下面的例子:


>>> %%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.loc['Albania', 1987, 'male', '25-34 years']CPU times: user 459 µs, sys: 1 µs, total: 460 µs

什么?加速 20 倍?


你要问自己了,创建这个多索引要多长时间?


%%timemi_df = df.set_index(['country', 'year', 'sex', 'age'])CPU times: user 10.8 ms, sys: 2.2 ms, total: 13 ms


通过查询访问数据的时间是 1.5 倍。如果你只想检索一次数据(这种情况很少发生),查询是正确的方法。否则,你一定要坚持用索引,CPU 会为此感激你的。


.set_index(drop=False) 允许不删除用作新索引的列。


.loc[]/.iloc[] 方法可以很好地读取数据框,但无法修改数据框。如果需要手动构建(比如使用循环),那就要考虑其他的数据结构了(比如字典、列表等),在准备好所有数据后,创建 DataFrame。否则,对于 DataFrame 中的每一个新行,Pandas 都会更新索引,这可不是简单的哈希映射。


>>> (pd.DataFrame({'a':range(2), 'b': range(2)}, index=['a', 'a']) .loc['a'])   a ba 0 0a 1 1


因此,未排序的索引可以降低性能。为了检查索引是否已经排序并对它排序,主要有两种方法:


%%time>>> mi_df.sort_index()CPU times: user 34.8 ms, sys: 1.63 ms, total: 36.5 ms>>> mi_df






请到「今天看啥」查看全文