22  连接 2:一对多、多键连接与键不匹配

22.1

import pandas as pd
import country_converter as cc

22.2 数据

运行以下代码以加载和定义本课程中使用的数据集。

# 加载数据集
oil_consumption = pd.read_csv(
    "https://raw.githubusercontent.com/the-graph-courses/idap_book/main/data/oil_consumption.csv"
)
tidyr_population = pd.read_csv(
    "https://raw.githubusercontent.com/the-graph-courses/idap_book/main/data/tidyr_population.csv"
)
country_regions = pd.read_csv(
    "https://raw.githubusercontent.com/the-graph-courses/idap_book/main/data/country_continent_data.csv"
)


oil_2012 = (
    oil_consumption[oil_consumption["year"] == 2012].copy().drop(columns=["year"])
)

# 人员数据
people = pd.DataFrame({"name": ["Alice", "Bob", "Charlie"], "age": [25, 32, 45]})

test_info_many = pd.DataFrame(
    {
        "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",
        ],
    }
)

farm_info = pd.DataFrame(
    {
        "farm_id": [1, 2, 3],
        "farm_name": ["Green Acres", "Harvest Hill", "Golden Fields"],
        "location": ["County A", "County B", "County A"],
    }
)

crop_yields = pd.DataFrame(
    {
        "farm_id": [1, 1, 2, 3, 3],
        "crop": ["Wheat", "Corn", "Soybeans", "Wheat", "Barley"],
        "yield_tons": [50, 60, 45, 55, 30],
    }
)

traffic_flow = pd.DataFrame(
    {
        "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],
    }
)

pollution_levels = pd.DataFrame(
    {
        "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],
    }
)

test_info_diff = pd.DataFrame(
    {
        "name": ["alice", "Bob", "Charlie "],
        "test_date": ["2023-06-05", "2023-08-10", "2023-05-02"],
        "result": ["Negative", "Positive", "Negative"],
    }
)

asia_countries = pd.DataFrame(
    {
        "Country": ["India", "Indonesia", "Philippines"],
        "Capital": ["New Delhi", "Jakarta", "Manila"],
    }
)

asia_population = pd.DataFrame(
    {
        "Country": ["India", "indonesia", "Philipines"],
        "Population": [1393000000, 273500000, 113000000],
        "Life_Expectancy": [69.7, 71.7, 72.7],
    }
)

22.3 介绍

现在我们对不同类型的连接及其工作原理有了扎实的掌握,我们可以看看如何管理更复杂的连接和更混乱的数据。

22.4 学习目标

  • 您了解一对多连接的概念

  • 您知道如何在多个键列上进行连接

  • 您知道如何检查数据框之间的不匹配值

22.5 一对多连接

到目前为止,我们主要研究了一对一连接,其中一个数据框中的一个观测值对应另一个数据框中的仅一个观测值。在一对多连接中,一个数据框中的一个观测值对应另一个数据框中的多个观测值。

为了说明一对多连接,让我们回到患者及其 COVID 测试数据。假设在我们的数据集中,AliceXavier 多次接受了 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() 时会发生什么:

pd.merge(people, test_info_many, on="name", how="left")
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 上进行连接。让我们试一下,看看会发生什么:

pd.merge(traffic_flow, pollution_levels, on="street_name", how="left")
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_nametime_of_day。为此,我们需要通过在列表中指定两个列名,告诉 Python 要在两列上匹配。

pd.merge(traffic_flow, pollution_levels, on=["street_name", "time_of_day"])
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:包含年度人口数据
# 查看数据集
oil_consumption.sort_values(by=["country", "year"])
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

tidyr_population.sort_values(by=["country", "year"])
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

  1. 使用左连接的 merge() 连接这些数据集。由于我们想匹配国家和年份,您需要在多个列上进行连接。(您可能会注意到并非所有行都匹配。您可以暂时忽略这一点。)

  2. 连接后,创建一个名为 consumption_per_capita 的新列,计算每人的年度油耗(吨)。

  3. 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()

people.merge(test_info_diff, on='name', how='left')
name age test_date result
0 Alice 25 NaN NaN
1 Bob 32 2023-08-10 Positive
2 Charlie 45 NaN NaN
pd.merge(people, test_info_diff, on="name", how="inner")
name age test_date result
0 Bob 32 2023-08-10 Positive

如我们所见,Python 没有将 Alicealice 识别为同一个人,并且它也无法匹配 CharlieCharlie!因此,在左连接中我们失去了 AliceCharlie,在内连接中它们被删除了。

我们如何解决这个问题?我们需要确保两个数据集中的名称格式相同。为此,我们可以使用 str.title() 将每个名称的首字母大写。

test_info_diff['name'] = test_info_diff['name'].str.title()
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
people.merge(test_info_diff, on='name', how='inner')
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() 将其转换为数组来更好地发现这一点:

test_info_diff['name'].unique()
array(['Alice', 'Bob', 'Charlie '], dtype=object)

我们可以使用 str.strip() 来移除额外的空格。

test_info_diff['name'] = test_info_diff['name'].str.strip()
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

现在我们可以连接这两个数据集:

people.merge(test_info_diff, on='name', how='inner')
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(
    oil_consumption, tidyr_population, on=["country", "year"], how="left"
).sort_values(["country", "year"])
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

这是因为两个数据集中的国家名称格式不一致。

在尝试连接这些数据集之前,最好检查键列中的不匹配。这可以帮助您识别可能阻止成功连接的任何差异。

首先,让我们识别两个数据集中唯一的国家名称。

oil_countries = set(oil_consumption['country'].unique())
pop_countries = set(tidyr_population['country'].unique())

现在,要查找在 oil_consumption 中但不在 tidyr_population 中的国家,我们可以使用集合运算:

missing_in_pop = oil_countries - pop_countries
missing_in_pop
{'Hong Kong, China',
 'Iran',
 'North Macedonia',
 'Russia',
 'Slovak Republic',
 'South Korea',
 'Taiwan',
 'UK',
 'USA',
 'Venezuela',
 'Vietnam'}

以及在 tidyr_population 中但不在 oil_consumption 中的国家:

missing_in_oil = pop_countries - oil_countries
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 = set([1, 2, 3, 4, 5])
set_2 = set([2, 3, 4])

我们可以通过集合运算检查 set_1 中不在 set_2 中的值:

set_1 - set_2
{1, 5}

以及使用以下方法检查 set_2 中不在 set_1 中的值:

set_2 - set_1
set()

22.12.1 使用国家代码进行合并

为了避免国家不匹配,通常使用国家代码而不是国家名称作为键会很有用。

现在,让我们向两个数据集中添加国家代码并再次尝试连接。

# 如何使用 country_converter
cc.convert("Nigeria", to='ISO3')
'NGA'
oil_consumption['country_code'] = cc.convert(oil_consumption['country'], to='ISO3')
tidyr_population['country_code'] = cc.convert(tidyr_population['country'], to='ISO3')
oil_pop_code = oil_consumption.merge(tidyr_population, on=['country_code', 'year'], how='left')

22.12.2 识别剩余的不匹配

让我们看看哪些国家仍未找到匹配:

set(oil_pop_code['country_code'].unique()) - set(tidyr_population['country_code'].unique())
{'TWN'}

似乎 ‘TWN’(台湾)未能找到匹配。我们可以手动查看 tidyr_population 数据集,看看是否能找到它。

tidyr_population.query("country.str.contains('Taiwan')")
country year population country_code

为了防止大小写不匹配,我们还可以检查 ‘taiwan’:

tidyr_population.query("country.str.contains('taiwan')")
country year population country_code

我们还可以检查 ‘China’,因为目前关于台湾是否属于中国存在争议。

tidyr_population.query("country.str.contains('China')")
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

使用国家代码作为键连接这两个数据集。然后找出每个大陆油耗最高的国家。作为合理性检查,您的答案应包括美国和中国。

oil_2012['country_code'] = cc.convert(oil_2012['country'], to='ISO3')

oil_2012_regions = oil_2012.merge(country_regions, on='country_code', how='left')

max_oil_by_continent = oil_2012_regions.loc[
    oil_2012_regions.groupby('continent')['oil_consump'].idxmax()
]

max_oil_by_continent[['country', 'continent', 'oil_consump']]
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 中的数据框连接相关的几个高级概念:

  1. 一对多关系:我们学习了当一个数据框中的一个观测值对应另一个数据框中的多个观测值时,连接是如何工作的,以及“单”方的数据如何为“多”方的每个匹配行重复。

  2. 多键连接:我们发现了如何使用多个列作为键进行数据框连接(例如,将街道名称与时间点结合),这对于在单个列不足以唯一标识观测值时保持数据完整性至关重要。

  3. 键不匹配:我们探讨了从不同来源连接数据时常见的挑战,包括:

    • 大小写敏感问题(例如,“Alice” vs “alice”)
    • 尾随空格
    • 拼写变化
    • 使用标准化代码(如国家代码)以确保匹配一致性

这些技能对于实际的数据分析至关重要,因为现实中数据通常来自多个来源,需要在有效组合之前进行仔细的清理和准备。