Python大数据分析7:数据的连接

前面我们所介绍的各种数据处理都在一个数据表格中进行。在真实的各种应用中,我们也需要处理来自于不同数据表格的内容。此时,就需要对这些数据进行连接,才能继续进一步的操作。

比如,我们再新建一个DataFrame,保存所有的学生课程的成绩。

# coding:utf-8
import pandas as pd
from pandas import DataFrame

data1 = {'ID': ['000001', '000002', '000003', '000004', '000005', '000006', '000007'],
         'name': ['黎明', '赵怡春', '张富平', '白丽', '牛玉德', '姚华', '李南'],
         'gender': [True, False, True, False, True, False, True],
         'age': [16, 20, 18, 18, 17, 18, 16],
         'height': [1.88, 1.78, 1.81, 1.86, 1.74, 1.75, 1.76]
         }
students = pd.DataFrame(data1)
data2 = {
    'SID': ['000001', '000001', '000002', '000003', '000003', '000003', '000004', '000004', '000005', '000006',
            '000006'],
    'CID': ['A01', 'A02', 'A01', 'A01', 'A02', 'B01', 'A01', 'A03', 'B01', 'A02', 'B01'],
    'score': [56, 78, 90, 74, 86, 89, 67, 80, 77, 76, 90]
}
scores = pd.DataFrame(data2)
print(pd.merge(students, scores, left_on='ID', right_on='SID'))

这里有三列,分别是学号SID,和前面学生的ID对应,然后是课程号CID,以及当前学生该课程的成绩score。

具体构造方法依然使用和构建学生数据的方式一样,这里不再细说。

我们先尝试这将这两个数据连接起来看看是什么样子?

print(pd.merge(students, scores, left_on=’ID’, right_on=’SID’))

显示效果很清楚,因此在单独的成绩数据中,只能看到每个学生的学号,但是通过连接后,我们可以看到更多对应的完整学生信息:

这个merge函数就是合并的意思,第一个和第二个参数分别表示要合并的两个DataFrame,通常称第一个为左数据,第二个称为右数据,相应的,第三个和第四个参数分别代表着依据左数据的哪个列和右数据的哪个列按照相等的原则进行连接。

需要补充说明下,如果两个数据含有的关联列名称一样,这个时候可以省略对关联所需列的说明。比如将data2的SID换成ID,就可以直接书写:

print(pd.merge(students, scores))

从结果来看,自动按照相同列连接,而且还自动去除一个多余的同名连接列。这种连接也被称为自然连接。

连接后的表格可以进行各种常见的数据选择等操作。这里有一个潜在的问题,如果有来自于不同数据表格的两个列具有一样的名称,此时数据选择会出错,因此,遇到相同列名称的时候,可以增加一个连接参数indicator,当它为真时,系统会自动对重名列分别加上不同的后缀来做区分。比如我们把成绩表格的course列名称改为name,此时就可以通过name_x获得左数据(x)的列:

print(pd.merge(students, scores, left_on=’ID’, right_on=’SID’, indicator=True))

为了后续操作方便,我们仍然保留CID的课程代码,细心的同学也会发现,这张表中有一个学生没有出现,对!那就是学号为000007的学生,因为没有选修的课程成绩,所以连接没有显示。但是有时我们也希望即使没有对应的成绩,也可以连接,那么这种扩展的连接称之为外连接。于此对应,现在这种默认的连接称之为内连接。外连接由于涉及两个数据表格,因此要说明是以左数据全部显示为准,还是以右数据全部显示为准。这里由于以学生左数据为准,所以为左外连接:

print(pd.merge(students, scores, left_on=’ID’, right_on=’SID’, how=’left’))

这个how参数就是指左外连接。由于000007学生没有选修课程,因此对应的课程和成绩都为空值,在pandas中,NaN就表示空值,它的字面意思是指“不是一个数(Not a Number)”。

大家可能也会觉得这好像没有什么意义。其实这种特殊的外连接往往可以起到非常有效的查询效果。比如查询哪些学生没有选修课程,我们就可以看出此时课程或者成绩为空的就是这样的学生,于是我们可以写出完整的查询:

frame = pd.merge(students, scores, left_on=’ID’, right_on=’SID’, how=’left’)

print(frame[pd.isna(frame[‘score’]) == True][‘name’])

我们首先将连接后的DataFrame保存下来,然后再利用以前的数据选择方法即可获得该学生的姓名。

这里要补充说明几个问题:

一是空值是什么?空值其实并不是没有值,而是有值但是无法知道。因此,在很多时候,我们都需要单独考虑空值可能产生的影响,同时也不建议大家随意保留空值。

二是判断是否为空值,正确的语法是使用pandas的isna函数或者isnull函数。它返回是一个真或者假。因此也可以简写为:

print(frame[pd.isna(frame[‘score’])][‘name’])

除了能按照列进行连接外,还可以按照索引号进行连接。此时可以使用join函数方法。Merge也可以实现,不过,join更为方便。大家要注意,merge为pandas工具包的函数方法,而join是DataFrame的函数方法,哪个DataFrame调用就以哪个为主进行以索引号相等为条件的外连接。

为此,我们重新设定了成绩表格的所有索引号,并进行了以学生表格为主的join:

scores.index = [0, 0, 1, 2, 2, 2, 3, 3, 4, 5, 5]

print(students.join(scores))

结果和刚才merge的左外连接没有区别。

请注意这个为主的含义,如比我们调换join前后的数据:

print(scores.join(students))

此时可以看到没有7号学生记录,因为该学生没有成绩,成绩为主的连接将不采用该记录。

最后我们通过一个结合分组和聚合统计运算的例子来看看。比如我们想知道每位学生的姓名和平均分:

frame = pd.merge(students, scores, left_on=’ID’, right_on=’SID’)

print(frame[[‘score’]].groupby(frame[‘name’]).mean())

大家能看得出这个方法的特点吗?首先需要连接,我们通过frame保存。然后选择分组的依据,以groupby表示,然后选择聚合运算,这需要两个方面:一是在前面可以指定聚合的字段,后面指定聚合的方法。

当然,我们也经常看到这样的写法:

print(frame.groupby(frame[‘name’])[[‘score’]].mean())

除了性能有所差异,功能没有差异。这样的写法会意味着Frame将整体性的参与分组,显然计算开销更大,虽然看起来似乎更好理解一些。

好了,到此为此,第一阶段的Pandas数据分析基本方法都已经介绍完毕,我们下面就以结合一些案例来真正的分析下数据了。请大家这段时间好好练习基本功!

发表评论

邮箱地址不会被公开。 必填项已用*标注