2012 年 Wes McKinney 一本经典的 Python for Data Analysis 由 O'Reilly 出版的。Pandas 参考书 现在有一点过时了 ,他最近宣布新版的新书即将出版 我还是觉得这是一本 瞭解 Pandas 如何作用的必备书 我也欣赏一本更简要的书
Learning the Pandas Library 由 Matt Harrison 著作 这不是一本有关资料分析及统计的综合书籍 但如果您只是要学习基本的 Pandas 想要很快的上手
Marco Rodriguez 跟 Tim Golden 维护了一个 很棒的部落格称为 Planet Python 您可以到 planetpython.org 这个网站, 订阅 RSS 或者从 推特(Twitter) 的 @PlanetPython 得到最新的文章 里面有很多 Python 资料科学的贡献者 我也强烈建议您订阅 RSS
Kyle Polich 运作一个称为 Data Skeptic 的 podcast 它并不是专门讲 Python 但它的制作精良 有很棒的跟这个领域专家座谈 以及 简短的教育课程 大部分的字眼是 有关于机器学习方法 但如果您计画 进一步探索这个学程 这个课程也在其中, 我真的很建议您订阅这个 podcast
The Series Data Structure
传递一个 list 的值来新创一个 Series, 当这麽做时, Pandas 会自动从零开始分配索引 并将该series的名称设置为“None”。
传入一些资料 索引和名称 资料可以是任何东西,类似阵列(array),像list一样。
pandas自动的识别了类型 在list中包含的数据,在这裡我们传入string列表, pandas将这类型设定为object。
In [1]:
import pandas as pd
import numpy as np
In [2]:
animals = ['Tiger', 'Bear', 'Moose','Bear']
pd.Series(animals)
Out[2]:
0 Tiger
1 Bear
2 Moose
3 Bear
dtype: object
In [3]:
numbers = [1, 2, 3]
pd.Series(numbers)
Out[3]:
0 1
1 2
2 3
dtype: int64
不一定要使用strings。 如果传入整数列表, 可以看见Pandas设定类型为int64。 在Pandas内部储存series的值,使用NumPy程式库的类型阵列(typed array)。 在处理数据时这提供显著的加速相比于传统 Python的list
NumPy 和 Pandas 如何处理遗失的资料: 在Python中,有none type以表示资料缺失。
In [4]:
animals = ['Tiger', 'Bear', None]
pd.Series(animals)
Out[4]:
0 Tiger
1 Bear
2 None
dtype: object
In [5]:
numbers = [1, 2, None]
pd.Series(numbers)
Out[5]:
0 1.0
1 2.0
2 NaN
dtype: float64
- 事实上不能对Nan本身的做相等测试。 因为答案总是错误的。
- 需要使用特殊功能来测试 '不是一个数字'的存在,例如numpy程式库中的isnan。
In [6]:
np.nan == None
Out[6]:
False
In [7]:
np.nan == np.nan
Out[7]:
False
In [8]:
np.isnan(np.nan)
Out[8]:
True
In [9]:
sports = {'Archery': 'Bhutan',
'Golf': 'Scotland',
'Sumo': 'Japan',
'Taekwondo': 'South Korea'}
s = pd.Series(sports)
s
Out[9]:
Archery Bhutan
Golf Scotland
Sumo Japan
Taekwondo South Korea
dtype: object
In [10]:
s.index
Out[10]:
Index(['Archery', 'Golf', 'Sumo', 'Taekwondo'], dtype='object')
In [11]:
s = pd.Series(['Tiger', 'Bear', 'Moose'], index=['India', 'America', 'Canada'])
s
Out[11]:
India Tiger
America Bear
Canada Moose
dtype: object
In [12]:
sports = {'Archery': 'Bhutan',
'Golf': 'Scotland',
'Sumo': 'Japan',
'Taekwondo': 'South Korea'}
s = pd.Series(sports, index=['Golf', 'Sumo', 'Hockey'])
s
Out[12]:
Golf Scotland
Sumo Japan
Hockey NaN
dtype: objec
- series创建完成后, 可以使用index属性获取index对象。
- 也可以将index的创建与数据分离,通过 将index作为列表,明确地传递给series。
那麽如果index中的值列表 与dictionary中用于创建该系列的keys不对齐
- pandas会覆盖自动创建index值,仅只用 你提供的所有的index值。
- 会忽略你的dictionary中所有的keys,当keys不在你的index中,pandas将添加non类型或NaN
Querying a Series
Pandas的Series(列表)可以查询,使用索引(index)的位置或索引的标签(label)。
要利用数位位置查询,从零开始,使用
iloc
属性。要通过索引标签(label)进行查询,可以使用
loc
属性。以下是维基百科的全国体育赛事数据。假设我们想要列出所有的运动当我们的索引(index),和 国家列表作为值(value)。
sports = {'Archery': 'Bhutan',
'Golf': 'Scotland',
'Sumo': 'Japan',
'Taekwondo': 'South Korea'}
s = pd.Series(sports)
s
>>>
Archery Bhutan
Golf Scotland
Sumo Japan
Taekwondo South Korea
dtype: object
s.iloc[3]
>>> 'South Korea'
s.loc['Golf']
>>> 'Scotland'
s[3]
>>> 'South Korea'
s['Golf']
>>> 'Scotland'
- 请记住,iloc和loc不是方法(method),是属性(attribute)。所以不用括号()来查询它们,而是使用方括号[], 我们称之为索引运算符。在Python中, 这是获取(get)和设置(set)一个项目的方法,根据其使用的背景来决定。
这看起来可能有点困惑的,如果你习惯于语言在哪里封装在里面的 属性、变数和性能是常见的,比如在JAVA中。 Pandas试图使我们的程式更具有可读性,并提供一种 智慧语法,使用index操作符直接在series本身。
最后两行指令:例如,如果你传入一个整数参数, 运算子会表现得好像你想要通过iloc属性来查询。如果你传入一个物件(object), 它将认为你想要查询使用根据标签(label)的loc属性。
- 那么如果你的index是整数列表会发生什么呢?这有点复杂,pandas无法自动确定 你是打算通过索引位置或索引标签进行查询。所以在series本身使用index操作时,你需要小心。而更安全的选择是更加明确,直接使用 iloc或loc属性。
sports = {99: 'Bhutan',
100: 'Scotland',
101: 'Japan',
102: 'South Korea'}
s = pd.Series(sports)
s[0]
#This won't call s.iloc[0] as one might expect, it generates an error instead
一个典型的程式设计方法,要遍历 该series中的所有项目,并调用一个你感兴趣的运算: 例如,我们可以创建一个浮点值的数据组(dataframe)。让我们把这些看作是不同产品的价格。我们可以写一个小的例行程序码,遍历的所有 series中的项目,并将它们加一起以获得总数。
Pandas和基础的NumPy程式库支持一个称为 向量化
vectorization.
Vectorization与NumPy库中的大部分功能一起使用, 包括sum函数。
调用np.sum 并传入一个可遍历迭代的项目。在这里,我们的pandas series。
s = pd.Series([100.00, 120.00, 101.00, 3.00])
s
>>>
0 100.0
1 120.0
2 101.0
3 3.0
dtype: float64
total = 0
for item in s:
total+=item
print(total)
>>> 324.0
total = np.sum(s)
print(total)
>>> 324.0
现在这两种方法产生相同的值,但是哪一种是确实更快吗?
首先,设置一个大系列的随机(random)数字。
神奇功能以百分比符号%开头。如果我们打入这个标志%,然后按Tab键, 我们可以看到可用的魔术函数的列表。你也可以编写自己的魔术函数 但这不过是本课程的范围之外。我们实际上会
使用所谓的细胞(cellular)魔术函数 -- 以两个百分比的符号开始, 并修改或包装当前Jupyter单元中的程式。
** 要使用的函数称为timeit。你可能已经从名称猜到了,此函数会运行我们的程式几次来确定,平均运行时间。
#this creates a big series of random numbers
s = pd.Series(np.random.randint(0,1000,10000))
s.head()
%%timeit -n 100
summary = 0
for item in s:
summary+=item
>>> 100 loops, best of 3: 1.87 ms per loop
%%timeit -n 100
summary = np.sum(s)
>>>100 loops, best of 3: 107 µs per loop
- 在pandas和NumPy的相关的功能称为广播(broadcasting)。通过broadcasting,你可以对series中的每个值应用操作, 更改series。
- 例如,如果我们想要对每个随机变数增加2, 我们可以使用+=运算符号直接在列表对像上快速地执行。在这里,我只需要使用head运算印出前五项 首先,我想要先来介绍这门课的四位讲师
- 做这的样程序方式是,遍历所有的 列表中的项目和直接增加它的数值。很快的提一下, Pandas确实支持遍历迭代列表项目,很类似于dictionary, 让你容易地把数值分拆开。但如果你发现自己反覆遍历一列表, 你应该质疑你做的方式是否是尽可能最好的。
%%timeit -n 10
s = pd.Series(np.random.randint(0,1000,10000))
for label, value in s.iteritems():
s.loc[label]= value+2
>>>
10 loops, best of 3: 1.65 s per loop
%%timeit -n 10
s = pd.Series(np.random.randint(0,1000,10000))
s+=2
>>>
10 loops, best of 3: 514 µs per loop
- 最后一点要注意的,在使用索引运算来存取列表资料。 .loc属性(attribute) 不仅可以修改数据, 还可以添加新数据。如果作为索引传入的值不存在,则它会添加一个新条目。请记住,指数可以有混合类型。虽然重要的是,要注意在下面的类型是什么, Pandas会根据需要,自动更改基本的NumPy类型。
s = pd.Series([1, 2, 3])
s.loc['Animal'] = 'Bears'
s
>>>
0 1
1 2
2 3
Animal Bears
dtype: object
original_sports = pd.Series({'Archery': 'Bhutan',
'Golf': 'Scotland',
'Sumo': 'Japan',
'Taekwondo': 'South Korea'})
cricket_loving_countries = pd.Series(['Australia',
'Barbados',
'Pakistan',
'England'],
index=['Cricket',
'Cricket',
'Cricket',
'Cricket'])
all_countries = original_sports.append(cricket_loving_countries)
original_sports
Archery Bhutan
Golf Scotland
Sumo Japan
Taekwondo South Korea
dtype: object
cricket_loving_countries
Cricket Australia
Cricket Barbados
Cricket Pakistan
Cricket England
dtype: object
all_countries
Archery Bhutan
Golf Scotland
Sumo Japan
Taekwondo South Korea
Cricket Australia
Cricket Barbados
Cricket Pakistan
Cricket England
dtype: object
all_countries.loc['Cricket']
Cricket Australia
Cricket Barbados
Cricket Pakistan
Cricket England
dtype: object
- 我们回到我们原来的运动列表。可以创建一个带有多个条目的新列表,用 板球索引,然后使用append将它们放在一起。使用append时,有几个重要的注意事项。首先,Pandas将采取你的列表, 并尝试推断使用最好的数据类型。在这个例子中,一切都是字符(string),所以这里没有问题。
- 其次,append方法实际上并没有改变底层的列表。而是返回一个由两个附加在一起组成的新列表。我们可以回溯并列印原始列表值, 看到它们没有改变。
这是实际上的一个重大问题 新的Pandas使用者,之前习惯了物件(objects)在原处更改。所以要当心了,不只是append,还有其他的Pandas函数功能。
最后,我们看到,当我们查询附加在一起的列表,用板球 作为国家运动的,我们不是得到一个单一的值,而是一个列表。这实际上是很常见的。
The DataFrame Data Structure
DataFrame数据结构是Pandas的核心。
DataFrame在概念上是一个二维列表(series)对象, 其中有一个索引(index)和多列内容,每列(column)都有一个标签(label)。事实上,列(column)和行(row)之间的区别 实际上只是一个概念上的区别。可以将DataFrame本身视为简单的双轴有标签的阵列。
- 可以以许多不同的方式创建一个DataFrame。例如,你可以使用一组列表(series), 其中每个列表代表一行数据。或者你可以使用一组字典(dictionary), 其中每个字典都代表一行数据。
purchase_1 = pd.Series({'Name': 'Chris',
'Item Purchased': 'Dog Food',
'Cost': 22.50})
purchase_2 = pd.Series({'Name': 'Kevyn',
'Item Purchased': 'Kitty Litter',
'Cost': 2.50})
purchase_3 = pd.Series({'Name': 'Vinod',
'Item Purchased': 'Bird Seed',
'Cost': 5.00})
df = pd.DataFrame([purchase_1, purchase_2, purchase_3], index=['Store 1', 'Store 1', 'Store 2'])
df.head()
>>>
Cost Item Purchased Name
Store 1 22.5 Dog Food Chris
Store 1 2.5 Kitty Litter Kevyn
Store 2 5.0 Bird Seed Vinod
- 与列表(series)类似,可以使用iloc和loc属性提取数据。因为DataFrame是二维的,所以将单一值传递给loc 索引操作将返回一个列表,如果只有一行返回。
df.loc['Store 2']
>>>
Cost 5
Item Purchased Bird Seed
Name Vinod
Name: Store 2, dtype: object
- 列表的名称作为行索引值返回, 而列名(column_name)也包括在输出中。