import pandas as pd
import country_converter as cc
22 连接 2:一对多、多键连接与键不匹配
22.1 包
22.2 数据
运行以下代码以加载和定义本课程中使用的数据集。
# 加载数据集
= pd.read_csv(
oil_consumption "https://raw.githubusercontent.com/the-graph-courses/idap_book/main/data/oil_consumption.csv"
)= pd.read_csv(
tidyr_population "https://raw.githubusercontent.com/the-graph-courses/idap_book/main/data/tidyr_population.csv"
)= pd.read_csv(
country_regions "https://raw.githubusercontent.com/the-graph-courses/idap_book/main/data/country_continent_data.csv"
)
= (
oil_2012 "year"] == 2012].copy().drop(columns=["year"])
oil_consumption[oil_consumption[
)
# 人员数据
= pd.DataFrame({"name": ["Alice", "Bob", "Charlie"], "age": [25, 32, 45]})
people
= pd.DataFrame(
test_info_many
{"name": ["Alice", "Alice", "Bob", "Bob", "Charlie", "Charlie"],
"test_date": [
"2023-06-05",
"2023-06-10",
"2023-08-10",
"2023-05-02",
"2023-05-12",
"2023-05-15",
],"result": [
"Negative",
"Positive",
"Positive",
"Negative",
"Negative",
"Negative",
],
}
)
= pd.DataFrame(
farm_info
{"farm_id": [1, 2, 3],
"farm_name": ["Green Acres", "Harvest Hill", "Golden Fields"],
"location": ["County A", "County B", "County A"],
}
)
= pd.DataFrame(
crop_yields
{"farm_id": [1, 1, 2, 3, 3],
"crop": ["Wheat", "Corn", "Soybeans", "Wheat", "Barley"],
"yield_tons": [50, 60, 45, 55, 30],
}
)
= pd.DataFrame(
traffic_flow
{"street_name": [
"Main St",
"Main St",
"Broadway",
"Broadway",
"Elm St",
"Elm St",
],"time_of_day": ["9am", "2pm", "9am", "2pm", "9am", "2pm"],
"vehicle_count": [1200, 900, 1500, 1100, 700, 600],
}
)
= pd.DataFrame(
pollution_levels
{"street_name": [
"Main St",
"Main St",
"Broadway",
"Broadway",
"Elm St",
"Elm St",
],"time_of_day": ["9am", "2pm", "9am", "2pm", "9am", "2pm"],
"pm_2_5_level": [35.5, 42.1, 40.3, 48.2, 25.7, 30.9],
}
)
= pd.DataFrame(
test_info_diff
{"name": ["alice", "Bob", "Charlie "],
"test_date": ["2023-06-05", "2023-08-10", "2023-05-02"],
"result": ["Negative", "Positive", "Negative"],
}
)
= pd.DataFrame(
asia_countries
{"Country": ["India", "Indonesia", "Philippines"],
"Capital": ["New Delhi", "Jakarta", "Manila"],
}
)
= pd.DataFrame(
asia_population
{"Country": ["India", "indonesia", "Philipines"],
"Population": [1393000000, 273500000, 113000000],
"Life_Expectancy": [69.7, 71.7, 72.7],
} )
22.3 介绍
现在我们对不同类型的连接及其工作原理有了扎实的掌握,我们可以看看如何管理更复杂的连接和更混乱的数据。
22.4 学习目标
您了解一对多连接的概念
您知道如何在多个键列上进行连接
您知道如何检查数据框之间的不匹配值
22.5 一对多连接
到目前为止,我们主要研究了一对一连接,其中一个数据框中的一个观测值对应另一个数据框中的仅一个观测值。在一对多连接中,一个数据框中的一个观测值对应另一个数据框中的多个观测值。
为了说明一对多连接,让我们回到患者及其 COVID 测试数据。假设在我们的数据集中,Alice
和 Xavier
多次接受了 COVID 测试。我们可以在 test_info
数据框中添加两行他们的新测试信息:
people
name | age | |
---|---|---|
0 | Alice | 25 |
1 | Bob | 32 |
2 | Charlie | 45 |
test_info_many
name | test_date | result | |
---|---|---|---|
0 | Alice | 2023-06-05 | Negative |
1 | Alice | 2023-06-10 | Positive |
2 | Bob | 2023-08-10 | Positive |
3 | Bob | 2023-05-02 | Negative |
4 | Charlie | 2023-05-12 | Negative |
5 | Charlie | 2023-05-15 | Negative |
接下来,让我们看看当我们使用 people
作为左数据框进行 merge()
时会发生什么:
="name", how="left") pd.merge(people, test_info_many, on
name | age | test_date | result | |
---|---|---|---|---|
0 | Alice | 25 | 2023-06-05 | Negative |
1 | Alice | 25 | 2023-06-10 | Positive |
2 | Bob | 32 | 2023-08-10 | Positive |
3 | Bob | 32 | 2023-05-02 | Negative |
4 | Charlie | 45 | 2023-05-12 | Negative |
5 | Charlie | 45 | 2023-05-15 | Negative |
上面发生了什么?基本上,当您执行一对多连接时,“一”方的数据会为“多”方的每个匹配行重复。
22.6 练习题:合并一对多农作物产量
运行以下代码以打印两个小数据框:
farm_info
farm_id | farm_name | location | |
---|---|---|---|
0 | 1 | Green Acres | County A |
1 | 2 | Harvest Hill | County B |
2 | 3 | Golden Fields | County A |
crop_yields
farm_id | crop | yield_tons | |
---|---|---|---|
0 | 1 | Wheat | 50 |
1 | 1 | Corn | 60 |
2 | 2 | Soybeans | 45 |
3 | 3 | Wheat | 55 |
4 | 3 | Barley | 30 |
如果您使用 merge()
来连接这些数据集,最终的数据框中将有多少行?试着计算一下,然后执行连接以查看答案是否正确。
22.7 多键列
有时我们有不止一个列可以唯一标识我们想要匹配的观测值。例如,假设我们有三条街道在两个不同时间点(上午 9 点和下午 2 点)的交通流量数据。
traffic_flow
street_name | time_of_day | vehicle_count | |
---|---|---|---|
0 | Main St | 9am | 1200 |
1 | Main St | 2pm | 900 |
2 | Broadway | 9am | 1500 |
3 | Broadway | 2pm | 1100 |
4 | Elm St | 9am | 700 |
5 | Elm St | 2pm | 600 |
现在,假设我们有另一组针对相同三条街道在同一时间点记录的空气污染水平(以颗粒物 PM2.5 衡量)数据。
pollution_levels
street_name | time_of_day | pm_2_5_level | |
---|---|---|---|
0 | Main St | 9am | 35.5 |
1 | Main St | 2pm | 42.1 |
2 | Broadway | 9am | 40.3 |
3 | Broadway | 2pm | 48.2 |
4 | Elm St | 9am | 25.7 |
5 | Elm St | 2pm | 30.9 |
我们想要连接这两个数据集,使每条街道有两行:一行对应上午 9 点,一行对应下午 2 点。为此,我们的第一个直觉可能是仅在 street_name
上进行连接。让我们试一下,看看会发生什么:
="street_name", how="left") pd.merge(traffic_flow, pollution_levels, on
street_name | time_of_day_x | vehicle_count | time_of_day_y | pm_2_5_level | |
---|---|---|---|---|---|
0 | Main St | 9am | 1200 | 9am | 35.5 |
1 | Main St | 9am | 1200 | 2pm | 42.1 |
2 | Main St | 2pm | 900 | 9am | 35.5 |
3 | Main St | 2pm | 900 | 2pm | 42.1 |
4 | Broadway | 9am | 1500 | 9am | 40.3 |
5 | Broadway | 9am | 1500 | 2pm | 48.2 |
6 | Broadway | 2pm | 1100 | 9am | 40.3 |
7 | Broadway | 2pm | 1100 | 2pm | 48.2 |
8 | Elm St | 9am | 700 | 9am | 25.7 |
9 | Elm St | 9am | 700 | 2pm | 30.9 |
10 | Elm St | 2pm | 600 | 9am | 25.7 |
11 | Elm St | 2pm | 600 | 2pm | 30.9 |
如我们所见,这完全不是我们想要的结果!我们得到了重复的行——每条街道现在有四行。
我们想要的是同时匹配 street_name
和 time_of_day
。为此,我们需要通过在列表中指定两个列名,告诉 Python 要在两列上匹配。
=["street_name", "time_of_day"]) pd.merge(traffic_flow, pollution_levels, on
street_name | time_of_day | vehicle_count | pm_2_5_level | |
---|---|---|---|---|
0 | Main St | 9am | 1200 | 35.5 |
1 | Main St | 2pm | 900 | 42.1 |
2 | Broadway | 9am | 1500 | 40.3 |
3 | Broadway | 2pm | 1100 | 48.2 |
4 | Elm St | 9am | 700 | 25.7 |
5 | Elm St | 2pm | 600 | 30.9 |
现在我们有了正确的行数!我们可以直接看到每条街道在每个时间点的车辆数量和 PM2.5 水平。
22.8 练习题:计算人均油耗
我们有两个包含国家信息的数据集:
oil_consumption
:包含年度油耗(吨)tidyr_population
:包含年度人口数据
# 查看数据集
=["country", "year"]) oil_consumption.sort_values(by
country | year | oil_consump | |
---|---|---|---|
19 | Algeria | 1995 | 8430000 |
98 | Algeria | 1996 | 8060000 |
177 | Algeria | 1997 | 7990000 |
256 | Algeria | 1998 | 8220000 |
335 | Algeria | 1999 | 8110000 |
... | ... | ... | ... |
1183 | Vietnam | 2009 | 14200000 |
1262 | Vietnam | 2010 | 15300000 |
1341 | Vietnam | 2011 | 16700000 |
1420 | Vietnam | 2012 | 17000000 |
1499 | Vietnam | 2013 | 18200000 |
1501 rows × 3 columns
=["country", "year"]) tidyr_population.sort_values(by
country | year | population | |
---|---|---|---|
0 | Afghanistan | 1995 | 17586073 |
1 | Afghanistan | 1996 | 18415307 |
2 | Afghanistan | 1997 | 19021226 |
3 | Afghanistan | 1998 | 19496836 |
4 | Afghanistan | 1999 | 19987071 |
... | ... | ... | ... |
4040 | Zimbabwe | 2009 | 12888918 |
4041 | Zimbabwe | 2010 | 13076978 |
4042 | Zimbabwe | 2011 | 13358738 |
4043 | Zimbabwe | 2012 | 13724317 |
4044 | Zimbabwe | 2013 | 14149648 |
4045 rows × 3 columns
使用左连接的
merge()
连接这些数据集。由于我们想匹配国家和年份,您需要在多个列上进行连接。(您可能会注意到并非所有行都匹配。您可以暂时忽略这一点。)连接后,创建一个名为
consumption_per_capita
的新列,计算每人的年度油耗(吨)。1995 年哪个国家的人均油耗最高?
22.9 键不匹配
在从不同来源提取数据后,您通常需要预先清理数据,才能进行连接。这是因为记录值的方式可能存在不一致。
举例来说,让我们回到第一课的模拟患者数据。如果您还记得,我们有两个数据框,一个名为 people
,另一个名为 test_info
。我们可以重新创建这些数据集,但将 test_info_diff
数据集中的 Alice
更改为 alice
,并保持所有其他值不变。
people
name | age | |
---|---|---|
0 | Alice | 25 |
1 | Bob | 32 |
2 | Charlie | 45 |
test_info_diff
name | test_date | result | |
---|---|---|---|
0 | alice | 2023-06-05 | Negative |
1 | Bob | 2023-08-10 | Positive |
2 | Charlie | 2023-05-02 | Negative |
现在让我们尝试对这两个数据集进行 merge()
。
='name', how='left') people.merge(test_info_diff, on
name | age | test_date | result | |
---|---|---|---|---|
0 | Alice | 25 | NaN | NaN |
1 | Bob | 32 | 2023-08-10 | Positive |
2 | Charlie | 45 | NaN | NaN |
="name", how="inner") pd.merge(people, test_info_diff, on
name | age | test_date | result | |
---|---|---|---|---|
0 | Bob | 32 | 2023-08-10 | Positive |
如我们所见,Python 没有将 Alice
和 alice
识别为同一个人,并且它也无法匹配 Charlie
和 Charlie
!因此,在左连接中我们失去了 Alice
和 Charlie
,在内连接中它们被删除了。
我们如何解决这个问题?我们需要确保两个数据集中的名称格式相同。为此,我们可以使用 str.title()
将每个名称的首字母大写。
'name'] = test_info_diff['name'].str.title()
test_info_diff[ test_info_diff
name | test_date | result | |
---|---|---|---|
0 | Alice | 2023-06-05 | Negative |
1 | Bob | 2023-08-10 | Positive |
2 | Charlie | 2023-05-02 | Negative |
='name', how='inner') people.merge(test_info_diff, on
name | age | test_date | result | |
---|---|---|---|---|
0 | Alice | 25 | 2023-06-05 | Negative |
1 | Bob | 32 | 2023-08-10 | Positive |
嗯,Charlie 仍然没有匹配。从打印输出中很难看出,但 test_info_diff
中的字符串 Charlie
在末尾有一个额外的空格。
我们可以通过使用 .unique()
将其转换为数组来更好地发现这一点:
'name'].unique() test_info_diff[
array(['Alice', 'Bob', 'Charlie '], dtype=object)
我们可以使用 str.strip()
来移除额外的空格。
'name'] = test_info_diff['name'].str.strip()
test_info_diff[ test_info_diff
name | test_date | result | |
---|---|---|---|
0 | Alice | 2023-06-05 | Negative |
1 | Bob | 2023-08-10 | Positive |
2 | Charlie | 2023-05-02 | Negative |
现在我们可以连接这两个数据集:
='name', how='inner') people.merge(test_info_diff, on
name | age | test_date | result | |
---|---|---|---|---|
0 | Alice | 25 | 2023-06-05 | Negative |
1 | Bob | 32 | 2023-08-10 | Positive |
2 | Charlie | 45 | 2023-05-02 | Negative |
完美!
22.10 练习题:内连接国家
以下两个数据集包含印度、印度尼西亚和菲律宾的数据。然而,这些数据集的 inner
连接仅返回 1 行。
asia_countries
Country | Capital | |
---|---|---|
0 | India | New Delhi |
1 | Indonesia | Jakarta |
2 | Philippines | Manila |
asia_population
Country | Population | Life_Expectancy | |
---|---|---|---|
0 | India | 1393000000 | 69.7 |
1 | indonesia | 273500000 | 71.7 |
2 | Philipines | 113000000 | 72.7 |
pd.merge(asia_countries, asia_population)
Country | Capital | Population | Life_Expectancy | |
---|---|---|---|---|
0 | India | New Delhi | 1393000000 | 69.7 |
键列中的值有哪些差异需要在连接数据集之前进行更改?请注意大小写和拼写。
现在,修复 Country
列中不匹配的值,然后再次尝试连接。
22.11 键不匹配:油耗示例
现在让我们看一个键不匹配如何导致问题的更现实的例子。
oil_consumption
country | year | oil_consump | |
---|---|---|---|
0 | United Arab Emirates | 1995 | 20800000 |
1 | Argentina | 1995 | 20300000 |
2 | Australia | 1995 | 36500000 |
3 | Austria | 1995 | 11300000 |
4 | Azerbaijan | 1995 | 6580000 |
... | ... | ... | ... |
1496 | USA | 2013 | 791000000 |
1497 | Uzbekistan | 2013 | 2860000 |
1498 | Venezuela | 2013 | 36800000 |
1499 | Vietnam | 2013 | 18200000 |
1500 | South Africa | 2013 | 26700000 |
1501 rows × 3 columns
tidyr_population
country | year | population | |
---|---|---|---|
0 | Afghanistan | 1995 | 17586073 |
1 | Afghanistan | 1996 | 18415307 |
2 | Afghanistan | 1997 | 19021226 |
3 | Afghanistan | 1998 | 19496836 |
4 | Afghanistan | 1999 | 19987071 |
... | ... | ... | ... |
4040 | Zimbabwe | 2009 | 12888918 |
4041 | Zimbabwe | 2010 | 13076978 |
4042 | Zimbabwe | 2011 | 13358738 |
4043 | Zimbabwe | 2012 | 13724317 |
4044 | Zimbabwe | 2013 | 14149648 |
4045 rows × 3 columns
尝试进行连接后,我们发现一些国家没有匹配,例如越南。
pd.merge(=["country", "year"], how="left"
oil_consumption, tidyr_population, on"country", "year"]) ).sort_values([
country | year | oil_consump | population | |
---|---|---|---|---|
19 | Algeria | 1995 | 8430000 | 29315463.0 |
98 | Algeria | 1996 | 8060000 | 29845208.0 |
177 | Algeria | 1997 | 7990000 | 30345466.0 |
256 | Algeria | 1998 | 8220000 | 30820435.0 |
335 | Algeria | 1999 | 8110000 | 31276295.0 |
... | ... | ... | ... | ... |
1183 | Vietnam | 2009 | 14200000 | NaN |
1262 | Vietnam | 2010 | 15300000 | NaN |
1341 | Vietnam | 2011 | 16700000 | NaN |
1420 | Vietnam | 2012 | 17000000 | NaN |
1499 | Vietnam | 2013 | 18200000 | NaN |
1501 rows × 4 columns
这是因为两个数据集中的国家名称格式不一致。
在尝试连接这些数据集之前,最好检查键列中的不匹配。这可以帮助您识别可能阻止成功连接的任何差异。
首先,让我们识别两个数据集中唯一的国家名称。
= set(oil_consumption['country'].unique())
oil_countries = set(tidyr_population['country'].unique()) pop_countries
现在,要查找在 oil_consumption
中但不在 tidyr_population
中的国家,我们可以使用集合运算:
= oil_countries - pop_countries
missing_in_pop missing_in_pop
{'Hong Kong, China',
'Iran',
'North Macedonia',
'Russia',
'Slovak Republic',
'South Korea',
'Taiwan',
'UK',
'USA',
'Venezuela',
'Vietnam'}
以及在 tidyr_population
中但不在 oil_consumption
中的国家:
= pop_countries - oil_countries
missing_in_oil missing_in_oil
{'Afghanistan',
'Albania',
'American Samoa',
'Andorra',
'Angola',
'Anguilla',
'Antigua and Barbuda',
'Armenia',
'Aruba',
'Bahamas',
'Bahrain',
'Barbados',
'Belize',
'Benin',
'Bermuda',
'Bhutan',
'Bolivia (Plurinational State of)',
'Bonaire, Saint Eustatius and Saba',
'Bosnia and Herzegovina',
'Botswana',
'British Virgin Islands',
'Brunei Darussalam',
'Burkina Faso',
'Burundi',
'Cabo Verde',
'Cambodia',
'Cameroon',
'Cayman Islands',
'Central African Republic',
'Chad',
'China, Hong Kong SAR',
'China, Macao SAR',
'Comoros',
'Congo',
'Cook Islands',
'Costa Rica',
'Cuba',
'Curaçao',
"Côte d'Ivoire",
"Democratic People's Republic of Korea",
'Democratic Republic of the Congo',
'Djibouti',
'Dominica',
'Dominican Republic',
'El Salvador',
'Equatorial Guinea',
'Eritrea',
'Ethiopia',
'Fiji',
'French Polynesia',
'Gabon',
'Gambia',
'Georgia',
'Ghana',
'Greenland',
'Grenada',
'Guam',
'Guatemala',
'Guinea',
'Guinea-Bissau',
'Guyana',
'Haiti',
'Honduras',
'Iran (Islamic Republic of)',
'Jamaica',
'Jordan',
'Kenya',
'Kiribati',
'Kyrgyzstan',
"Lao People's Democratic Republic",
'Lebanon',
'Lesotho',
'Liberia',
'Libya',
'Madagascar',
'Malawi',
'Maldives',
'Mali',
'Malta',
'Marshall Islands',
'Mauritania',
'Mauritius',
'Micronesia (Federated States of)',
'Monaco',
'Mongolia',
'Montenegro',
'Montserrat',
'Mozambique',
'Myanmar',
'Namibia',
'Nauru',
'Nepal',
'New Caledonia',
'Nicaragua',
'Niger',
'Nigeria',
'Niue',
'Northern Mariana Islands',
'Palau',
'Panama',
'Papua New Guinea',
'Paraguay',
'Puerto Rico',
'Republic of Korea',
'Republic of Moldova',
'Russian Federation',
'Rwanda',
'Saint Kitts and Nevis',
'Saint Lucia',
'Saint Vincent and the Grenadines',
'Samoa',
'San Marino',
'Sao Tome and Principe',
'Senegal',
'Serbia',
'Seychelles',
'Sierra Leone',
'Sint Maarten (Dutch part)',
'Slovakia',
'Solomon Islands',
'Somalia',
'South Sudan',
'Sudan',
'Suriname',
'Swaziland',
'Syrian Arab Republic',
'Tajikistan',
'The Former Yugoslav Republic of Macedonia',
'Timor-Leste',
'Togo',
'Tokelau',
'Tonga',
'Tunisia',
'Turks and Caicos Islands',
'Tuvalu',
'US Virgin Islands',
'Uganda',
'United Kingdom of Great Britain and Northern Ireland',
'United Republic of Tanzania',
'United States of America',
'Uruguay',
'Vanuatu',
'Venezuela (Bolivarian Republic of)',
'Viet Nam',
'Wallis and Futuna Islands',
'West Bank and Gaza Strip',
'Yemen',
'Zambia',
'Zimbabwe'}
这些差异表明键列中存在不匹配,需要在连接之前解决。
您可能会尝试手动检查。例如,我们可以看到越南在一个数据集中写作 Vietnam
,在另一个数据集中写作 Viet Nam
。
然而,对于国家来说,还有一个更好的解决方案:使用国家代码!我们将在下一节中看到如何做到这一点。
22.12 集合运算
对于不熟悉的人来说,快速介绍一下集合运算。
考虑两个数字集合 1:5 和 2:4。
= set([1, 2, 3, 4, 5])
set_1 = set([2, 3, 4]) set_2
我们可以通过集合运算检查 set_1
中不在 set_2
中的值:
- set_2 set_1
{1, 5}
以及使用以下方法检查 set_2
中不在 set_1
中的值:
- set_1 set_2
set()
22.12.1 使用国家代码进行合并
为了避免国家不匹配,通常使用国家代码而不是国家名称作为键会很有用。
现在,让我们向两个数据集中添加国家代码并再次尝试连接。
# 如何使用 country_converter
"Nigeria", to='ISO3') cc.convert(
'NGA'
'country_code'] = cc.convert(oil_consumption['country'], to='ISO3')
oil_consumption['country_code'] = cc.convert(tidyr_population['country'], to='ISO3') tidyr_population[
= oil_consumption.merge(tidyr_population, on=['country_code', 'year'], how='left') oil_pop_code
22.12.2 识别剩余的不匹配
让我们看看哪些国家仍未找到匹配:
set(oil_pop_code['country_code'].unique()) - set(tidyr_population['country_code'].unique())
{'TWN'}
似乎 ‘TWN’(台湾)未能找到匹配。我们可以手动查看 tidyr_population
数据集,看看是否能找到它。
"country.str.contains('Taiwan')") tidyr_population.query(
country | year | population | country_code |
---|
为了防止大小写不匹配,我们还可以检查 ‘taiwan’:
"country.str.contains('taiwan')") tidyr_population.query(
country | year | population | country_code |
---|
我们还可以检查 ‘China’,因为目前关于台湾是否属于中国存在争议。
"country.str.contains('China')") tidyr_population.query(
country | year | population | country_code | |
---|---|---|---|---|
783 | China | 1995 | 1237531429 | CHN |
784 | China | 1996 | 1247897092 | CHN |
785 | China | 1997 | 1257021784 | CHN |
786 | China | 1998 | 1265222536 | CHN |
787 | China | 1999 | 1272915272 | CHN |
788 | China | 2000 | 1280428583 | CHN |
789 | China | 2001 | 1287890449 | CHN |
790 | China | 2002 | 1295322020 | CHN |
791 | China | 2003 | 1302810258 | CHN |
792 | China | 2004 | 1310414386 | CHN |
793 | China | 2005 | 1318176835 | CHN |
794 | China | 2006 | 1326146433 | CHN |
795 | China | 2007 | 1334343509 | CHN |
796 | China | 2008 | 1342732604 | CHN |
797 | China | 2009 | 1351247555 | CHN |
798 | China | 2010 | 1359821465 | CHN |
799 | China | 2011 | 1368440300 | CHN |
800 | China | 2012 | 1377064907 | CHN |
801 | China | 2013 | 1385566537 | CHN |
802 | China, Hong Kong SAR | 1995 | 6144498 | HKG |
803 | China, Hong Kong SAR | 1996 | 6275363 | HKG |
804 | China, Hong Kong SAR | 1997 | 6430651 | HKG |
805 | China, Hong Kong SAR | 1998 | 6591717 | HKG |
806 | China, Hong Kong SAR | 1999 | 6732627 | HKG |
807 | China, Hong Kong SAR | 2000 | 6835301 | HKG |
808 | China, Hong Kong SAR | 2001 | 6892752 | HKG |
809 | China, Hong Kong SAR | 2002 | 6912079 | HKG |
810 | China, Hong Kong SAR | 2003 | 6906631 | HKG |
811 | China, Hong Kong SAR | 2004 | 6896523 | HKG |
812 | China, Hong Kong SAR | 2005 | 6896686 | HKG |
813 | China, Hong Kong SAR | 2006 | 6910671 | HKG |
814 | China, Hong Kong SAR | 2007 | 6934748 | HKG |
815 | China, Hong Kong SAR | 2008 | 6967866 | HKG |
816 | China, Hong Kong SAR | 2009 | 7006930 | HKG |
817 | China, Hong Kong SAR | 2010 | 7049514 | HKG |
818 | China, Hong Kong SAR | 2011 | 7096359 | HKG |
819 | China, Hong Kong SAR | 2012 | 7148493 | HKG |
820 | China, Hong Kong SAR | 2013 | 7203836 | HKG |
821 | China, Macao SAR | 1995 | 398459 | MAC |
822 | China, Macao SAR | 1996 | 405231 | MAC |
823 | China, Macao SAR | 1997 | 412031 | MAC |
824 | China, Macao SAR | 1998 | 418810 | MAC |
825 | China, Macao SAR | 1999 | 425448 | MAC |
826 | China, Macao SAR | 2000 | 431907 | MAC |
827 | China, Macao SAR | 2001 | 438080 | MAC |
828 | China, Macao SAR | 2002 | 444150 | MAC |
829 | China, Macao SAR | 2003 | 450711 | MAC |
830 | China, Macao SAR | 2004 | 458542 | MAC |
831 | China, Macao SAR | 2005 | 468149 | MAC |
832 | China, Macao SAR | 2006 | 479808 | MAC |
833 | China, Macao SAR | 2007 | 493206 | MAC |
834 | China, Macao SAR | 2008 | 507528 | MAC |
835 | China, Macao SAR | 2009 | 521617 | MAC |
836 | China, Macao SAR | 2010 | 534626 | MAC |
837 | China, Macao SAR | 2011 | 546278 | MAC |
838 | China, Macao SAR | 2012 | 556783 | MAC |
839 | China, Macao SAR | 2013 | 566375 | MAC |
似乎台湾不在 tidyr_population
数据集中。
在这种情况下,您可能会尝试找到包含台湾人口数据的数据集,并将其添加到 tidyr_population
数据集中。但我们留给您自行解决。
22.13 练习题:将油耗与地理数据合并
运行代码查看这两个数据集。
第一个数据集 oil_2012
记录了 2012 年的油耗:
oil_2012
country | oil_consump | |
---|---|---|
1343 | United Arab Emirates | 35200000 |
1344 | Argentina | 28600000 |
1345 | Australia | 46100000 |
1346 | Austria | 11900000 |
1347 | Azerbaijan | 4170000 |
... | ... | ... |
1417 | USA | 778000000 |
1418 | Uzbekistan | 3030000 |
1419 | Venezuela | 37200000 |
1420 | Vietnam | 17000000 |
1421 | South Africa | 26300000 |
79 rows × 2 columns
而 country_regions
列出了国家及其相应的地区和大陆:
country_regions
country_name | country_code | continent | |
---|---|---|---|
0 | Afghanistan | AFG | Asia |
1 | Albania | ALB | Europe |
2 | Algeria | DZA | Africa |
3 | American Samoa | ASM | Oceania |
4 | Andorra | AND | Europe |
... | ... | ... | ... |
237 | Western Sahara | ESH | Africa |
238 | Yemen | YEM | Asia |
239 | Zambia | ZMB | Africa |
240 | Zimbabwe | ZWE | Africa |
241 | Åland Islands | ALA | Europe |
242 rows × 3 columns
使用国家代码作为键连接这两个数据集。然后找出每个大陆油耗最高的国家。作为合理性检查,您的答案应包括美国和中国。
'country_code'] = cc.convert(oil_2012['country'], to='ISO3')
oil_2012[
= oil_2012.merge(country_regions, on='country_code', how='left')
oil_2012_regions
= oil_2012_regions.loc[
max_oil_by_continent 'continent')['oil_consump'].idxmax()
oil_2012_regions.groupby(
]
'country', 'continent', 'oil_consump']] max_oil_by_continent[[
country | continent | oil_consump | |
---|---|---|---|
21 | Egypt | Africa | 35300000 |
74 | USA | Americas | 778000000 |
13 | China | Asia | 484000000 |
62 | Russia | Europe | 145000000 |
2 | Australia | Oceania | 46100000 |
23 总结!
在本课程中,我们探讨了与 Python 中的数据框连接相关的几个高级概念:
一对多关系:我们学习了当一个数据框中的一个观测值对应另一个数据框中的多个观测值时,连接是如何工作的,以及“单”方的数据如何为“多”方的每个匹配行重复。
多键连接:我们发现了如何使用多个列作为键进行数据框连接(例如,将街道名称与时间点结合),这对于在单个列不足以唯一标识观测值时保持数据完整性至关重要。
键不匹配:我们探讨了从不同来源连接数据时常见的挑战,包括:
- 大小写敏感问题(例如,“Alice” vs “alice”)
- 尾随空格
- 拼写变化
- 使用标准化代码(如国家代码)以确保匹配一致性
这些技能对于实际的数据分析至关重要,因为现实中数据通常来自多个来源,需要在有效组合之前进行仔细的清理和准备。