无法播放?请 点击这里 跳转到Youtube
切换视频源:

通过上一章中的学习,我们简单的了解了什么是Pandas,以及Pandas的重要性,还通过一些代码的演示,直观地感受到了使用Pandas进行数据处理有多么方便。从这一章开始,我们就正式开始学习Pandas,那么在这一章中,我们先来弄明白 Pandas 中的最重要的两种数据结构 Series 和 DataFrame。

Series

由于 DataFrame 是由 Series 组成的,所以我们先来学 Series。

Series 是带标签的一维数组,可以用来存储整数、浮点数、字符串、Python 对象等类型的数据。其中的标签也可叫做索引(Index)

Pandas Series: abs() function - w3resource

以下是创建 Series 的语法:

import numpy as np
import pandas as pd

random_items = np.random.randint(25, size=(10)) # 10个0~24随机整数
series_data = pd.Series(random_items)
print(series_data)
0     5
1     3
2    15
3    11
4     2
5    23
6     6
7    19
8     6
9     4
dtype: int64

可以看到以上的Series中包含了10个随机整数,而每个随机整数都有相对应的标签,从0到9,如果想要获取特定的元素,我们可以使用类似Python List中的语法来获取元素:

series_data[0] # 获取数组中的第一个元素:5
series_data[:3] # 获取前三个元素
0     5
1     3
2    15
dtype: int64

而 Series 的 index 不仅仅只能是数字,我们也可以将字母当做index:

letter_indexs = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
new_series_data = pd.Series(random_items, index = letter_indexs)
a     5
b     3
c    15
d    11
e     2
f    23
g     6
h    19
i     6
j     4
dtype: int64

这样,我们的index和Series中的元素就有一一对应关系了,类似Python中的Dictionary,这样我们就可以使用类似Dictionary的语法来获得其中的元素:

new_series_data['b'] # 获取index为b的元素:3

创建Series的方式有很多,我们还能直接使用Dictionary创建Series:

data_dict = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
dict_series = pd.Series(data_dict)
print(dict_series)
a    1
b    2
c    3
d    4
e    5
dtype: int64

其中字典的key会变成index,而value则是key对应的具体数据。而且我们还能为Series命名:

dict_series = pd.Series(data_dict, name='series_name')
print(dict_series)
a    1
b    2
c    3
d    4
e    5
Name: series_name, dtype: int64

dict_series = dict_series.rename('new_name')
dict_series.name # 'new_name'

Series是不是功能很齐全呢。关于Series,最后再提一下索引,除了通过 index 进行索引之外,Series 还支持以下的骚操作:

dict_series[dict_series > dict_series.median()] # 获得大于数组中平均数的所有元素
d    4
e    5
dtype: int64

关于索引的部分,我们接下来在DataFrame中再展开讲。那Series其实很好理解,就是带有标签的一维数组,接下来来学习 DataFrame。

DataFrame

DataFrame 是由各种数据类型的列构成的、二维标签数据类型,类似 Excel、SQL中的表格。DataFrame是Pandas中最常用的数据结构,和Series一样,DataFrame支持多种类型的数据:

  • 列表、字典
  • 一维 ndarray
  • 二维 ndarray
  • Series
  • DataFrame
Python | Pandas DataFrame - GeeksforGeeks

和Series不同的是,DataFrame不仅仅有行标签(index),还有列标签(header)。行标签是用来定位行数据的,列标签顾名思义就是为了定义在特定列上的数据。

如何创建DataFrame呢?最简单的方式就是用字典创建:

d = {
    'one': [1, 2, 3, 4],
    'two': [4, 3, 2, 1]
}

dict_df = pd.DataFrame(d)
print(dict_df)
   one  two
0    1    4
1    2    3
2    3    2
3    4    1

可以看到上面这个DataFrame每行的数据都有对应的index,在这个例子中就是0, 1, 2, 3,而每一列都有对应的header,也就是one 和 two。而行index和列index就能帮助我们用来定位数据:

print(dict_df['one'])
0    1
1    2
2    3
3    4
Name: one, dtype: int64

可以看到 one 列的数据输出结果和Series数组很像,其实DataFrame就是由一堆Series的集合组成的,每一列数据就是一个Series:

type(dict_df['one']) # pandas.core.series.Series

如果想要查看 DataFrame 中有多少行和多少列,可以直接使用 shape attribute:

dict_df.shape # (4, 2)

除了通过Dictionary创建DataFrame外,我们也可以使用Series组成DataFrame:

d_data = {
    'one': pd.Series([1, 2, 3], name='col_one', index=['a', 'b', 'c']),
    'two': pd.Series([1, 2, 3, 4], name='col_two', index=['a', 'b', 'c', 'd'])
}

df = pd.DataFrame(d_data)
print(df)
   one  two
a  1.0    1
b  2.0    2
c  3.0    3
d  NaN    4

如果要重新设置index,我们可以直接使用 reset_index 函数:

new_df = df.reset_index()
print(new_df)
  index  one  two
0     a  1.0    1
1     b  2.0    2
2     c  3.0    3
3     d  NaN    4

new_df = df.reset_index(drop = True)
print(new_df)
   one  two
0  1.0    1
1  2.0    2
2  3.0    3
3  NaN    4

可以看到,使用reset_index之后,虽然新的DataFrame的index变成0~3了,但同时也多了一列数据,而这一列数据则是原来的index值。如果要忽视原来的index值,将drop定义成True即可。

但是有趣的是,原来的df其实并没有发生改变:

print(df)
   one  two
a  1.0    1
b  2.0    2
c  3.0    3
d  NaN    4

若要改变原来的DataFrame,我们需要将 inplace 定义成True:

df.reset_index(drop = True, inplace = True)
print(df)
   one  two
0  1.0    1
1  2.0    2
2  3.0    3
3  NaN    4

通过以上的学习,我们应该理解了DataFrame和Series之间的关系了。DataFrame无非就是将Series水平叠合在一起的表格罢了,然后每行每列都有其对应的标签。

接下来我们来简单学习一下如何索引DataFrame里面的元素,在下一章我们会继续深入探讨和索引相关的内容。

获取DataFrame元素

为了更好地理解什么是DataFrame,接下来我们通过一个更大的DataFrame来复习一下:

csv_data_path = 'https://raw.githubusercontent.com/turingplanet/pandas-intro/main/public-datasets/country_info.csv'
country_info = pd.read_csv(csv_data_path)

print(country_info.shape) # (227, 20)

print(country_info.columns)
Index(['Country', 'Region', 'Population', 'Area (sq. mi.)',
       'Pop. Density (per sq. mi.)', 'Coastline (coast/area ratio)',
       'Net migration', 'Infant mortality (per 1000 births)',
       'GDP ($ per capita)', 'Literacy (%)', 'Phones (per 1000)', 'Arable (%)',
       'Crops (%)', 'Other (%)', 'Climate', 'Birthrate', 'Deathrate',
       'Agriculture', 'Industry', 'Service'],
      dtype='object')

这个一个很真实的数据集,其中包含了不同国家的基本信息,比如国家名字、领域、人口、GDP等等等等。通过shape属性,我看可以发现数据集有227行和20列。

如果我们只想查看前几行的数据,我们可以直接使用head(n)函数,查看前n行数据(如果n不定义的话,则会默认看前5行):

print(country_info.head(6))
           Country                               Region  Population  ...
0     Afghanistan         ASIA (EX. NEAR EAST)             31056997  ...
1         Albania   EASTERN EUROPE                          3581655  ...
2         Algeria   NORTHERN AFRICA                        32930091  ...
3  American Samoa   OCEANIA                                   57794  ...
4         Andorra   WESTERN EUROPE                            71201  ...
5          Angola   SUB-SAHARAN AFRICA                     12127071  ...

那来复习一下如何通过header获取特定列的数据,直接在DataFrame之后用方括号加上column的名字即可,如果要获取多列的数据,则需要一个inner list:

country_info['Country'] # 获取 Country 列的数据
0         Afghanistan 
1             Albania 
2             Algeria 
3      American Samoa 
4             Andorra 
            ...       
222         West Bank 
223    Western Sahara 
224             Yemen 
225            Zambia 
226          Zimbabwe 
Name: Country, Length: 227, dtype: object

country_info.Country # 和country_info['Country']相同,但是不推荐,因为可以会和Pandas的默认属性或函数相冲突

print(country_info[['Country', 'Region']]) # 通过inner list获取多列的数据
             Country                               Region
0       Afghanistan         ASIA (EX. NEAR EAST)         
1           Albania   EASTERN EUROPE                     
2           Algeria   NORTHERN AFRICA                    
3    American Samoa   OCEANIA                            
4           Andorra   WESTERN EUROPE                     
..               ...                                  ...
222       West Bank   NEAR EAST                          
223  Western Sahara   NORTHERN AFRICA                    
224           Yemen   NEAR EAST                          
225          Zambia   SUB-SAHARAN AFRICA                 
226        Zimbabwe   SUB-SAHARAN AFRICA     

如果我们要获取特定行的数据呢?可以直接使用 iloc 通过行数来定位数据:

print(country_info.iloc[100]) # 获取第101行的数据 (iloc: interger location)
Country                                                           Israel 
Region                                NEAR EAST                          
Population                                                        6352117
Area (sq. mi.)                                                      20770
Pop. Density (per sq. mi.)                                          305,8
Coastline (coast/area ratio)                                         1,31
Net migration                                                        0,68
Infant mortality (per 1000 births)                                   7,03
GDP ($ per capita)                                                  19800
Literacy (%)                                                         95,4
Phones (per 1000)                                                   462,3
Arable (%)                                                          16,39
Crops (%)                                                            4,17
Other (%)                                                           79,44
Climate                                                                 3
Birthrate                                                           17,97
Deathrate                                                            6,18
Agriculture                                                         0,026
Industry                                                            0,317
Service                                                             0,657
Name: 100, dtype: object

country_info.iloc[[2, 3]] # 通过inner list获取第三行和第四行的数据

print(country_info.iloc[[2, 3], [0, 3]]) # 获取第3,4行,第1列和第4列的元素
           Country  Area (sq. mi.)
2         Algeria          2381740
3  American Samoa              199

既然此数据集第一列的国家名都不一样,那么我们就可以将其国家名设为index:

country_info['Country'] = country_info['Country'].str.strip() # 把Country列中开头和尾部的空格先去掉
country_info.set_index(['Country'], drop = True, inplace = True)
print(country_info)
                                              Region  Population  ...
Country                                                            
Afghanistan            ASIA (EX. NEAR EAST)             31056997  ...
Albania          EASTERN EUROPE                          3581655  ...
Algeria          NORTHERN AFRICA                        32930091  ...
American Samoa   OCEANIA                                   57794  ...
Andorra          WESTERN EUROPE                            71201  ...
...                                              ...         ...  ...
West Bank        NEAR EAST                               2460492  ...
Western Sahara   NORTHERN AFRICA                          273008  ...
Yemen            NEAR EAST                              21456188  ...
Zambia           SUB-SAHARAN AFRICA                     11502010  ...
Zimbabwe         SUB-SAHARAN AFRICA                     12236805  ...

那么我们就可以好好利用index,通过Country名字进行索引了:

country_info.loc['China']  # 获取中国的数据
Region                                ASIA (EX. NEAR EAST)         
Population                                               1313973713
Area (sq. mi.)                                              9596960
Pop. Density (per sq. mi.)                                    136,9
Coastline (coast/area ratio)                                   0,15
Net migration                                                  -0,4
Infant mortality (per 1000 births)                            24,18
GDP ($ per capita)                                             5000
Literacy (%)                                                   90,9
Phones (per 1000)                                             266,7
Arable (%)                                                     15,4
Crops (%)                                                      1,25
Other (%)                                                     83,35
Climate                                                         1,5
Birthrate                                                     13,25
Deathrate                                                      6,97
Agriculture                                                   0,125
Industry                                                      0,473
Service                                                       0,403
Name: China, dtype: object

print(country_info.loc[['China', 'Japan']]) # 通过inner list获取中国和日本的数据
                                Region  Population  Area (sq. mi.)  \
Country                                                              
China    ASIA (EX. NEAR EAST)           1313973713         9596960   
Japan    ASIA (EX. NEAR EAST)            127463611          377835   

print(country_info.loc[['China', 'Japan'], ['Region', 'Population']]) # 只获取中国和日本的区域和人口数据
                                Region  Population
Country                                           
China    ASIA (EX. NEAR EAST)           1313973713
Japan    ASIA (EX. NEAR EAST)            127463611

除了以上的基本操作,我们还能通过以下loc更灵活地索引数据:

print(country_info.loc['China':'Japan', 'Region':'Deathrate']) # 中国到日本,Region到Deathrate的数据 (: Slicing)
                                                   Region  Population  \
Country                                                                 
China                       ASIA (EX. NEAR EAST)           1313973713   
Colombia                          LATIN AMER. & CARIB        43593035   
Comoros               SUB-SAHARAN AFRICA                       690948   
Congo, Dem. Rep.      SUB-SAHARAN AFRICA                     62660551   
Congo, Repub. of the  SUB-SAHARAN AFRICA                      3702314   
...                                                   ...         ...   
Isle of Man           WESTERN EUROPE                            75441   
Israel                NEAR EAST                               6352117   
Italy                 WESTERN EUROPE                         58133509   
Jamaica                           LATIN AMER. & CARIB         2758124   
Japan                       ASIA (EX. NEAR EAST)            127463611  

关于索引的部分,我们下一章在学习indexing和filtering会继续深入。那么以上就是 Series 和 DataFrame 的基本学习啦,相信大家已经明白了 DataFrame 就是类似二维表格的数据结构,我们可以使用 index 和 header 来定位特定的数据。而 Series 则是一维的数据,只不过每个元素都多了一个 index 而已。

课后作业

使用read_csv获取 country 数据,然后拿到前10行的数据,只选取Country、Brithrate 和 Service,将其中的数据变成DataFrame,并使用to_csv函数将结果存到本地的 country.csv 文件中(注意不要存index col)。

csv_data_path = 'https://raw.githubusercontent.com/turingplanet/pandas-intro/main/public-datasets/country_info.csv'
country_info = pd.read_csv(csv_data_path)
country_info.loc[:9, ['Country', 'Birthrate', 'Service']].to_csv('country.csv', index=False)