# 4.3.1 数据分组（问答库）

1. 【单选题·易】数据分组由数据框对象的哪个方法完成？
   A. `merge()`
   B. `concat()`
   C. `groupby()`
   D. `sort_values()`
   **答案：C**
   **解析：** 课程明确：数据分组由数据框对象的 `groupby()` 方法完成，参数为分组的列标签，生成一个分组对象。

2. 【单选题·易】调用 `df.groupby('分区')` 时，传入的参数 `'分区'` 表示什么？
   A. 分组后要执行的聚合函数名
   B. 分组的列标签
   C. 必须是整数行索引
   D. 必须传入该列的全部数据列表
   **答案：B**
   **解析：** `groupby()` 的参数是分组的列标签。按「分区」分组写作 `group = df.groupby('分区')`。

3. 【判断题·易】`groupby()` 的返回值是一个分组对象，而不是已经汇总好的数据框。
   **答案：√**
   **解析：** 课程写法为 `group = df.groupby('分区')`，随后再用 `ngroups`、`size()` 或遍历去查看分组。分组本身不直接给出合计。

4. 【判断题·易】分组对象的属性 `ngroups` 用来查看一共分成了多少组。
   **答案：√**
   **解析：** 课程示例：`group.ngroups` 结果为 3，表示数据框按分区被分成了 3 组。

5. 【多选题·中】生成分组对象后，下列哪些方式可以查看分组情况？
   A. 读取属性 `ngroups`，得到分组个数
   B. 调用 `size()`，查看各分组的数量
   C. 用 `for name, g in group:` 遍历各分组
   D. 调用 `drop()`，按组删除行
   **答案：ABC**
   **解析：** 课程给出三种查看方式：`ngroups`、`size()`、遍历。`drop()` 用于删除行或列，不是查看分组的方法。

6. 【单选题·中】执行下列代码，输出是什么？

```python
from pandas import DataFrame
df = DataFrame({
    '姓名': ['赵四', '钱多', '孙冰', '李萌', '周金', '吴达', '郑华', '王军'],
    '分区': ['天河', '黄埔', '越秀', '黄埔', '天河', '天河', '黄埔', '越秀'],
    '一季度': [2572, 2845, 2100, 4483, 4109, 2435, 4663, 4839],
    '二季度': [3291, 3161, 3371, 3810, 2322, 4863, 1831, 1214],
    '三季度': [4388, 3723, 3901, 1070, 3056, 1576, 1451, 3639],
    '四季度': [2672, 3663, 3024, 2312, 4066, 1934, 3456, 2649]
})
group = df.groupby('分区')
print(group.ngroups)
```

   A. 8（按每一行各成一组）
   B. 3（按分区取值天河、黄埔、越秀分成 3 组）
   C. 4（按四个季度分成 4 组）
   D. 因未调用聚合而报错
   **答案：B**
   **解析：** 「分区」列只有天河、黄埔、越秀三个不同取值，故 `ngroups` 为 3。`groupby()` 只分组，不要求立刻聚合。

7. 【单选题·中】沿用上题中的数据框与分组对象 `group`，执行 `print(group.size())` 后，「越秀」对应的数量是？
   A. 3
   B. 8
   C. 2
   D. 1
   **答案：C**
   **解析：** `size()` 查看各分组的数量。越秀有孙冰、王军共 2 人；天河 3 人（赵四、周金、吴达），黄埔 3 人（钱多、李萌、郑华）。

8. 【多选题·中】关于 `for name, g in group:` 遍历分组，下列说法正确的有：
   A. `name` 是分组名，本例即该组的分区值
   B. `g` 是该组对应的子数据框
   C. 子数据框 `g` 保留原行标签
   D. 遍历时会自动删掉「分区」列，只留下数值列
   **答案：ABC**
   **解析：** 课程打印结果显示：分组名分别为天河、越秀、黄埔；各组仍含「姓名」「分区」及四个季度列，且行标签保持为原数据框中的位置（如天河为 0、4、5）。D 错误。

9. 【判断题·中】执行 `df.groupby('分区')` 之后，原数据框 `df` 会被就地改写成 3 个独立对象。
   **答案：×**
   **解析：** `groupby()` 生成分组对象，不修改原始数据框。原 `df` 仍是 8 行完整表；各组明细要通过分组对象的 `size()` 或遍历查看。

10. 【单选题·难】执行下列代码后，第一次循环打印的分组名、该组人数、行标签分别是什么？

```python
from pandas import DataFrame
df = DataFrame({
    '姓名': ['赵四', '钱多', '孙冰', '李萌', '周金', '吴达', '郑华', '王军'],
    '分区': ['天河', '黄埔', '越秀', '黄埔', '天河', '天河', '黄埔', '越秀'],
    '一季度': [2572, 2845, 2100, 4483, 4109, 2435, 4663, 4839],
    '二季度': [3291, 3161, 3371, 3810, 2322, 4863, 1831, 1214],
    '三季度': [4388, 3723, 3901, 1070, 3056, 1576, 1451, 3639],
    '四季度': [2672, 3663, 3024, 2312, 4066, 1934, 3456, 2649]
})
group = df.groupby('分区')
for name, g in group:
    print(name, len(g), list(g.index))
    break
```

   A. 天河，3，`[0, 4, 5]`（赵四、周金、吴达）
   B. 黄埔，3，`[1, 3, 6]`（按首次出现顺序，黄埔排第二故先打印黄埔）
   C. 赵四，1，`[0]`（按姓名分组）
   D. 天河，3，`[0, 1, 2]`（行标签被重建为从 0 开始）
   **答案：A**
   **解析：** 课程遍历顺序为天河、越秀、黄埔，不是按首次出现顺序（天河、黄埔、越秀）。天河组 3 人，行标签仍为 0、4、5，不会重建。`break` 只取第一次循环。

11. 【简答题·中】请写出完整可运行代码：创建与课程相同的销售业绩数据框 `df`，按「分区」分组，打印分组个数以及各分组的数量。
   **答案：**

```python
from pandas import DataFrame
df = DataFrame({
    '姓名': ['赵四', '钱多', '孙冰', '李萌', '周金', '吴达', '郑华', '王军'],
    '分区': ['天河', '黄埔', '越秀', '黄埔', '天河', '天河', '黄埔', '越秀'],
    '一季度': [2572, 2845, 2100, 4483, 4109, 2435, 4663, 4839],
    '二季度': [3291, 3161, 3371, 3810, 2322, 4863, 1831, 1214],
    '三季度': [4388, 3723, 3901, 1070, 3056, 1576, 1451, 3639],
    '四季度': [2672, 3663, 3024, 2312, 4066, 1934, 3456, 2649]
})
group = df.groupby('分区')
print(group.ngroups)   # 3
print(group.size())    # 天河 3、越秀 2、黄埔 3
```

   **解析：** 评分要点：① 用字典创建数据框，列名与课程一致；② `groupby('分区')` 得到分组对象；③ 用 `ngroups` 看组数（3），用 `size()` 看各组人数。漏写创建数据框或把 `ngroups` 写成方法调用可酌情扣分。

12. 【简答题·难】结合下列代码，回答四个问题：（1）`groupby` 的参数为什么是字符串 `'分区'` 而不是 `df['分区']`？（2）`ngroups` 与 `size()` 各回答什么问题？（3）遍历时黄埔组有哪些人、行标签是什么？（4）遍历顺序是「天河、黄埔、越秀」还是「天河、越秀、黄埔」？与数据框中分区值首次出现的顺序是否相同？

```python
from pandas import DataFrame
df = DataFrame({
    '姓名': ['赵四', '钱多', '孙冰', '李萌', '周金', '吴达', '郑华', '王军'],
    '分区': ['天河', '黄埔', '越秀', '黄埔', '天河', '天河', '黄埔', '越秀'],
    '一季度': [2572, 2845, 2100, 4483, 4109, 2435, 4663, 4839],
    '二季度': [3291, 3161, 3371, 3810, 2322, 4863, 1831, 1214],
    '三季度': [4388, 3723, 3901, 1070, 3056, 1576, 1451, 3639],
    '四季度': [2672, 3663, 3024, 2312, 4066, 1934, 3456, 2649]
})
group = df.groupby('分区')
print(group.ngroups)
print(group.size())
for name, g in group:
    print('分组名：', name)
    print(g)
```

   **答案：**
   （1）`groupby()` 的参数是分组的列标签，应传入列名字符串 `'分区'`，而不是该列的数据本身。
   （2）`ngroups` 回答「分成了几组」（本例为 3）；`size()` 回答「每组有多少行」（天河 3、越秀 2、黄埔 3）。
   （3）黄埔组为钱多、李萌、郑华，行标签 1、3、6，子数据框保留原行标签。
   （4）课程演示的遍历顺序是天河、越秀、黄埔，与首次出现顺序（天河、黄埔、越秀）不同。
   **解析：** 评分要点：① 点明参数是列标签；② 区分组数与各组人数；③ 黄埔三人及行标签正确；④ 能指出遍历顺序不是首次出现顺序。四点答全给满分。
