通过上一章中的学习,我们简单的了解了什么是Pandas,以及Pandas的重要性,还通过一些代码的演示,直观地感受到了使用Pandas进行数据处理有多么方便。从这一章开始,我们就正式开始学习Pandas,那么在这一章中,我们先来弄明白 Pandas 中的最重要的两种数据结构 Series 和 DataFrame。
Series
由于 DataFrame 是由 Series 组成的,所以我们先来学 Series。
Series 是带标签的一维数组,可以用来存储整数、浮点数、字符串、Python 对象等类型的数据。其中的标签也可叫做索引(Index)。
以下是创建 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
和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)