# 五、表格
> 原文:[Tables](https://github.com/data-8/textbook/tree/gh-pages/chapters/05)
> 译者:[飞龙](https://github.com/wizardforcel)
> 协议:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)
> 自豪地采用[谷歌翻译](https://translate.google.cn/)
表格是表示数据集的基本对象类型。 表格可以用两种方式查看:
+ 具名列的序列,每列都描述数据集中所有条目的一个方面,或者
+ 行的序列,每行都包含数据集中单个条目的所有信息。
为了使用表格,导入所有称为`datascience`的模块,这是为这篇文章创建的模块。
```py
from datascience import *
```
空表格可以使用`Table`创建。空表格是实用的,因为他可以扩展来包含新行和新列。
```py
Table()
```
表格上的`with_columns`方法使用带有附加标签的列,构造一个新表。 表格的每一列都是一个数组。 为了将一个新列添加到表中,请使用标签和数组调用`with_columns`。 (`with_column`方法具有相同的效果。)
下面,我们用一个没有列的空表开始每个例子。
```py
Table().with_columns('Number of petals', make_array(8, 34, 5))
```
| Number of petals |
| --- |
| 8 |
| 34 |
| 5 |
为了添加两个(或更多)新列,请为每列提供一个数组和标签。 所有列必须具有相同的长度,否则会发生错误。
```py
Table().with_columns(
'Number of petals', make_array(8, 34, 5),
'Name', make_array('lotus', 'sunflower', 'rose')
)
```
| Number of petals | Name |
| --- | --- |
| 8 | lotus |
| 34 | sunflower |
| 5 | rose |
我们可以给这个表格一个名词,之后使用另外一列扩展表格。
```py
flowers = Table().with_columns(
'Number of petals', make_array(8, 34, 5),
'Name', make_array('lotus', 'sunflower', 'rose')
)
flowers.with_columns(
'Color', make_array('pink', 'yellow', 'red')
)
```
| Number of petals | Name | Color |
| --- | --- | --- |
| 8 | lotus | pink |
| 34 | sunflower | yellow |
| 5 | rose | red |
`with_columns`方法每次调用时,都会创建一个新表,所以原始表不受影响。 例如,表`an_example`仍然只有它创建时的两列。
```
flowers
```
| Number of petals | Name |
| --- | --- |
| 8 | lotus |
| 34 | sunflower |
| 5 | rose |
通过这种方式创建表涉及大量的输入。 如果数据已经输入到某个地方,通常可以使用 Python 将其读入表格中,而不是逐个单元格地输入。
通常,表格从包含逗号分隔值的文件创建。这些文件被称为 CSV 文件。
下面,我们使用`Table`的`read_table`方法,来读取一个 CSV 文件,它包含了一些数据,Minard 在他的拿破仑的俄罗斯战役的图片中使用。 数据放在名为`minard`的表中。
```
minard = Table.read_table('minard.csv')
minard
```
| Longitude | Latitude | City | Direction | Survivors |
| --- | --- | --- | --- | --- |
| 32 | 54.8 | Smolensk | Advance | 145000 |
| 33.2 | 54.9 | Dorogobouge | Advance | 140000 |
| 34.4 | 55.5 | Chjat | Advance | 127100 |
| 37.6 | 55.8 | Moscou | Advance | 100000 |
| 34.3 | 55.2 | Wixma | Retreat | 55000 |
| 32 | 54.6 | Smolensk | Retreat | 24000 |
| 30.4 | 54.4 | Orscha | Retreat | 20000 |
| 26.8 | 54.3 | Moiodexno | Retreat | 12000 |
我们将使用这个小的表格来演示一些有用的表格方法。 然后,我们将使用这些相同的方法,并在更大的数据表上开发其他方法。
### 表格的大小
`num_columns `方法提供了表中的列数量,`num_rows`是行数量。
```py
minard.num_columns
5
minard.num_rows
8
```
### 列标签
`labels `方法可以用来列出所有列的标签。 对于`minard`,并不是特别有用,但是对于那些非常大的表格,并不是所有的列都在屏幕上可见。
```py
minard.labels
('Longitude', 'Latitude', 'City', 'Direction', 'Survivors')
```
我们使用`relabeled `修改列标签。这会创建新的表格,并保留`minard`不变。
```
minard.relabeled('City', 'City Name')
```
| Longitude | Latitude | City Name | Direction | Survivors |
| --- | --- | --- | --- | --- |
| 32 | 54.8 | Smolensk | Advance | 145000 |
| 33.2 | 54.9 | Dorogobouge | Advance | 140000 |
| 34.4 | 55.5 | Chjat | Advance | 127100 |
| 37.6 | 55.8 | Moscou | Advance | 100000 |
| 34.3 | 55.2 | Wixma | Retreat | 55000 |
| 32 | 54.6 | Smolensk | Retreat | 24000 |
| 30.4 | 54.4 | Orscha | Retreat | 20000 |
| 26.8 | 54.3 | Moiodexno | Retreat | 12000 |
但是,这个方法并不修改原始表。
```
minard
```
| Longitude | Latitude | City | Direction | Survivors |
| --- | --- | --- | --- | --- |
| 32 | 54.8 | Smolensk | Advance | 145000 |
| 33.2 | 54.9 | Dorogobouge | Advance | 140000 |
| 34.4 | 55.5 | Chjat | Advance | 127100 |
| 37.6 | 55.8 | Moscou | Advance | 100000 |
| 34.3 | 55.2 | Wixma | Retreat | 55000 |
| 32 | 54.6 | Smolensk | Retreat | 24000 |
| 30.4 | 54.4 | Orscha | Retreat | 20000 |
| 26.8 | 54.3 | Moiodexno | Retreat | 12000 |
常见的模式时将原始名称`minard`赋给新的表,以便`minard `未来的所有使用,都会引用修改标签的表格。
```py
minard = minard.relabeled('City', 'City Name')
minard
```
| Longitude | Latitude | City Name | Direction | Survivors |
| --- | --- | --- | --- | --- |
| 32 | 54.8 | Smolensk | Advance | 145000 |
| 33.2 | 54.9 | Dorogobouge | Advance | 140000 |
| 34.4 | 55.5 | Chjat | Advance | 127100 |
| 37.6 | 55.8 | Moscou | Advance | 100000 |
| 34.3 | 55.2 | Wixma | Retreat | 55000 |
| 32 | 54.6 | Smolensk | Retreat | 24000 |
| 30.4 | 54.4 | Orscha | Retreat | 20000 |
| 26.8 | 54.3 | Moiodexno | Retreat | 12000 |
### 访问列中的数据
我们可以使用列标签来访问列中的数据数组。
```py
minard.column('Survivors')
array([145000, 140000, 127100, 100000, 55000, 24000, 20000, 12000])
```
五列的下标分别为`0, 1, 2, 3, 4`。`Survivors`列也可以使用列下标来访问。
```py
minard.column(4)
array([145000, 140000, 127100, 100000, 55000, 24000, 20000, 12000])
```
数组中的八个条目下标为`0, 1, 2, ..., 7`。列中的条目可以使用`item`访问,就像任何数组那样。
```py
minard.column(4).item(0)
145000
minard.column(4).item(5)
24000
```
### 处理列中的数据
因为列是数组,所以我们可以使用数组操作来探索新的信息。 例如,我们可以创建一个新列,其中包含 Smolensk 之后每个城市的所有幸存者的百分比。
```py
initial = minard.column('Survivors').item(0)
minard = minard.with_columns(
'Percent Surviving', minard.column('Survivors')/initial
)
minard
```
| Longitude | Latitude | City Name | Direction | Survivors | Percent Surviving |
| --- | --- | --- | --- | --- | --- |
| 32 | 54.8 | Smolensk | Advance | 145000 | 100.00% |
| 33.2 | 54.9 | Dorogobouge | Advance | 140000 | 96.55% |
| 34.4 | 55.5 | Chjat | Advance | 127100 | 87.66% |
| 37.6 | 55.8 | Moscou | Advance | 100000 | 68.97% |
| 34.3 | 55.2 | Wixma | Retreat | 55000 | 37.93% |
| 32 | 54.6 | Smolensk | Retreat | 24000 | 16.55% |
| 30.4 | 54.4 | Orscha | Retreat | 20000 | 13.79% |
| 26.8 | 54.3 | Moiodexno | Retreat | 12000 | 8.28% |
要使新列中的比例显示为百分比,我们可以使用选项`PercentFormatter`调用`set_format`方法。 `set_format`方法接受`Formatter `对象,存在日期(`DateFormatter`),货币(`CurrencyFormatter`),数字和百分比。
```py
minard.set_format('Percent Surviving', PercentFormatter)
```
| Longitude | Latitude | City Name | Direction | Survivors | Percent Surviving |
| --- | --- | --- | --- | --- | --- |
| 32 | 54.8 | Smolensk | Advance | 145000 | 100.00% |
| 33.2 | 54.9 | Dorogobouge | Advance | 140000 | 96.55% |
| 34.4 | 55.5 | Chjat | Advance | 127100 | 87.66% |
| 37.6 | 55.8 | Moscou | Advance | 100000 | 68.97% |
| 34.3 | 55.2 | Wixma | Retreat | 55000 | 37.93% |
| 32 | 54.6 | Smolensk | Retreat | 24000 | 16.55% |
| 30.4 | 54.4 | Orscha | Retreat | 20000 | 13.79% |
| 26.8 | 54.3 | Moiodexno | Retreat | 12000 | 8.28% |
### 选择列的集合
`select `方法创建一个新表,仅仅包含指定的列。
```py
minard.select('Longitude', 'Latitude')
```
| Longitude | Latitude |
| --- | --- |
| 32 | 54.8 |
| 33.2 | 54.9 |
| 34.4 | 55.5 |
| 37.6 | 55.8 |
| 34.3 | 55.2 |
| 32 | 54.6 |
| 30.4 | 54.4 |
| 26.8 | 54.3 |
使用列索引而不是标签,也可以执行相同选择。
```py
minard.select(0, 1)
```
| Longitude | Latitude |
| --- | --- |
| 32 | 54.8 |
| 33.2 | 54.9 |
| 34.4 | 55.5 |
| 37.6 | 55.8 |
| 34.3 | 55.2 |
| 32 | 54.6 |
| 30.4 | 54.4 |
| 26.8 | 54.3 |
`select `的结果是个新表,即使当你选择一列时也是这样。
```
minard.select('Survivors')
```
| Survivors |
| --- |
| 145000 |
| 140000 |
| 127100 |
| 100000 |
| 55000 |
| 24000 |
| 20000 |
| 12000 |
要注意结果是个表格,不像`column`的结果,它是个数组。
```py
minard.column('Survivors')
array([145000, 140000, 127100, 100000, 55000, 24000, 20000, 12000])
```
另一种创建新表,包含列集合的方式,是`drop`你不想要的列。
```py
minard.drop('Longitude', 'Latitude', 'Direction')
```
| City Name | Survivors | Percent Surviving |
| --- | --- | --- |
| Smolensk | 145000 | 100.00% |
| Dorogobouge | 140000 | 96.55% |
| Chjat | 127100 | 87.66% |
| Moscou | 100000 | 68.97% |
| Wixma | 55000 | 37.93% |
| Smolensk | 24000 | 16.55% |
| Orscha | 20000 | 13.79% |
| Moiodexno | 12000 | 8.28% |
`select `和`drop`都不修改原始表格。 相反,他们创建了共享相同数据的新小型表格。 保留的原始表格是实用的! 你可以生成多个不同的表格,只考虑某些列,而不用担心会互相影响。
```
minard
```
| Longitude | Latitude | City Name | Direction | Survivors | Percent Surviving |
| --- | --- | --- | --- | --- | --- |
| 32 | 54.8 | Smolensk | Advance | 145000 | 100.00% |
| 33.2 | 54.9 | Dorogobouge | Advance | 140000 | 96.55% |
| 34.4 | 55.5 | Chjat | Advance | 127100 | 87.66% |
| 37.6 | 55.8 | Moscou | Advance | 100000 | 68.97% |
| 34.3 | 55.2 | Wixma | Retreat | 55000 | 37.93% |
| 32 | 54.6 | Smolensk | Retreat | 24000 | 16.55% |
| 30.4 | 54.4 | Orscha | Retreat | 20000 | 13.79% |
| 26.8 | 54.3 | Moiodexno | Retreat | 12000 | 8.28% |
我们用过的所有方法都可以用在任何表格上。
## 对行排序
CNN 在 2016 年 3 月报道说:“NBA 是全球薪水最高的职业体育联盟。”`nba_salaries`包含了 2015~2016 年间所有 NBA 球员的薪水。
每行表示一个球员。列为:
| 列标签 | 描述 |
| --- | --- |
| `PLAYER` | 球员名称 |
| `POSITION `| 球员在队里的位置 |
| `TEAM` | 队的明确 |
| `'15-'16 SALARY` | 2015~2016 年的球员薪水,单位是百万美元。 |
位置代码是 PG(控球后卫),SG(得分后卫),PF(大前锋),SF(小前锋)和 C(中锋)。 但接下来的内容并不涉及篮球运动的细节。
第一行显示,亚特兰大老鹰队(Atlanta Hawks)的大前锋保罗·米尔萨普(Paul Millsap)在 2015~2016 年间的薪水接近 1870 万美元。
```py
# This table can be found online: https://www.statcrunch.com/app/index.php?dataid=1843341
nba_salaries = Table.read_table('nba_salaries.csv')
nba_salaries
```
| `PLAYER` | `POSITION` | `TEAM` | `'15-'16 SALARY` |
| --- | --- | --- | --- |
| Paul Millsap | PF | Atlanta Hawks | 18.6717 |
| Al Horford | C | Atlanta Hawks | 12 |
| Tiago Splitter | C | Atlanta Hawks | 9.75625 |
| Jeff Teague | PG | Atlanta Hawks | 8 |
| Kyle Korver | SG | Atlanta Hawks | 5.74648 |
| Thabo Sefolosha | SF | Atlanta Hawks | 4 |
| Mike Scott | PF | Atlanta Hawks | 3.33333 |
| Kent Bazemore | SF | Atlanta Hawks | 2 |
| Dennis Schroder | PG | Atlanta Hawks | 1.7634 |
| Tim Hardaway Jr. | SG | Atlanta Hawks | 1.30452 |
(省略了 407 行)
该表包含 417 行,每个球员一行。 只显示了 10 行。`show`方法允许我们指定行数,缺省值(没有指定)是表的所有行。
```py
nba_salaries.show(3)
```
| `PLAYER` | `POSITION` | `TEAM` | `'15-'16 SALARY` |
| --- | --- | --- | --- |
| Paul Millsap | PF | Atlanta Hawks | 18.6717 |
| Al Horford | C | Atlanta Hawks | 12 |
| Tiago Splitter | C | Atlanta Hawks | 9.75625 |
(省略了 414 行)
通过浏览大约 20 行左右,你会看到行按字母顺序排列。 也可以使用`sort`方法,按球员姓名的字母顺序列出相同的行。 `sort`的参数是列标签或索引。
```py
nba_salaries.sort('PLAYER').show(5)
```
| `PLAYER` | `POSITION` | `TEAM` | `'15-'16 SALARY` |
| --- | --- | --- | --- |
| Aaron Brooks | PG | Chicago Bulls | 2.25 |
| Aaron Gordon | PF | Orlando Magic | 4.17168 |
| Aaron Harrison | SG | Charlotte Hornets | 0.525093 |
| Adreian Payne | PF | Minnesota Timberwolves | 1.93884 |
| Al Horford | C | Atlanta Hawks | 12 |
(省略了 412 行)
440/5000
为了检查球员的薪水,如果数据是按薪水排序的话,会更有帮助。
为了实现它,我们首先简化薪水列的标签(只是为了方便),然后用新的标签`SALARY`进行排序。
这会按照薪水的升序排列表中的所有行,最低的薪水在最前面。 输出是一个新表,列与原始表格相同,但行是重新排列的。
```py
nba = nba_salaries.relabeled("'15-'16 SALARY", 'SALARY')
nba.sort('SALARY')
```
| `PLAYER` | `POSITION` | `TEAM` | `'15-'16 SALARY` |
| --- | --- | --- | --- |
| Thanasis Antetokounmpo | SF | New York Knicks | 0.030888 |
| Jordan McRae | SG | Phoenix Suns | 0.049709 |
| Cory Jefferson | PF | Phoenix Suns | 0.049709 |
| Elliot Williams | SG | Memphis Grizzlies | 0.055722 |
| Orlando Johnson | SG | Phoenix Suns | 0.055722 |
| Phil Pressey | PG | Phoenix Suns | 0.055722 |
| Keith Appling | PG | Orlando Magic | 0.061776 |
| Sean Kilpatrick | SG | Denver Nuggets | 0.099418 |
| Erick Green | PG | Utah Jazz | 0.099418 |
| Jeff Ayres | PF | Los Angeles Clippers | 0.111444 |
(省略了 407 行)
这些数字有些难以比较,因为这些球员中的一些,在赛季中改变了球队,并从不止一支球队获得了薪水。 只有最后一支球队的薪水出现在表中。 控球后卫菲尔·普莱西(Phil Pressey)在年内从费城搬到了凤凰城,可能会再次转到金州勇士队(Golden State Warriors)。
CNN 的报道是薪酬水平的另一端 - 那些在世界上薪水最高的球员。
为了按照薪水的降序对表格的行排序,我们必须以`descending=True`调用`sort`函数。
```py
nba.sort('SALARY', descending=True)
```
| `PLAYER` | `POSITION` | `TEAM` | `'15-'16 SALARY` |
| --- | --- | --- | --- |
| Kobe Bryant | SF | Los Angeles Lakers | 25 |
| Joe Johnson | SF | Brooklyn Nets | 24.8949 |
| LeBron James | SF | Cleveland Cavaliers | 22.9705 |
| Carmelo Anthony | SF | New York Knicks | 22.875 |
| Dwight Howard | C | Houston Rockets | 22.3594 |
| Chris Bosh | PF | Miami Heat | 22.1927 |
| Chris Paul | PG | Los Angeles Clippers | 21.4687 |
| Kevin Durant | SF | Oklahoma City Thunder | 20.1586 |
| Derrick Rose | PG | Chicago Bulls | 20.0931 |
| Dwyane Wade | SG | Miami Heat | 20 |
(省略了 407 行)
科比(Kobe Bryant)在湖人队(Lakers)的最后一个赛季是薪水最高的,2500 万美元。 请注意,MVP 斯蒂芬·库里(Stephen Curry)并没有出现在前 10 名之列。他排在后面,我们将在后面看到。
### 具名参数
这个调用表达式的`descending = True`部分称为具名参数。 调用一个函数或方法时,每个参数都有一个位置和一个名字。 从函数或方法的帮助文本中都可以看出它们。
```py
help(nba.sort)
Help on method sort in module datascience.tables:
sort(column_or_label, descending=False, distinct=False) method of datascience.tables.Table instance
Return a Table of rows sorted according to the values in a column.
Args:
``column_or_label``: the column whose values are used for sorting.
``descending``: if True, sorting will be in descending, rather than
ascending order.
``distinct``: if True, repeated values in ``column_or_label`` will
be omitted.
Returns:
An instance of ``Table`` containing rows sorted based on the values
in ``column_or_label``.
>>> marbles = Table().with_columns(
... "Color", make_array("Red", "Green", "Blue", "Red", "Green", "Green"),
... "Shape", make_array("Round", "Rectangular", "Rectangular", "Round", "Rectangular", "Round"),
... "Amount", make_array(4, 6, 12, 7, 9, 2),
... "Price", make_array(1.30, 1.30, 2.00, 1.75, 1.40, 1.00))
>>> marbles
Color | Shape | Amount | Price
Red | Round | 4 | 1.3
Green | Rectangular | 6 | 1.3
Blue | Rectangular | 12 | 2
Red | Round | 7 | 1.75
Green | Rectangular | 9 | 1.4
Green | Round | 2 | 1
>>> marbles.sort("Amount")
Color | Shape | Amount | Price
Green | Round | 2 | 1
Red | Round | 4 | 1.3
Green | Rectangular | 6 | 1.3
Red | Round | 7 | 1.75
Green | Rectangular | 9 | 1.4
Blue | Rectangular | 12 | 2
>>> marbles.sort("Amount", descending = True)
Color | Shape | Amount | Price
Blue | Rectangular | 12 | 2
Green | Rectangular | 9 | 1.4
Red | Round | 7 | 1.75
Green | Rectangular | 6 | 1.3
Red | Round | 4 | 1.3
Green | Round | 2 | 1
>>> marbles.sort(3) # the Price column
Color | Shape | Amount | Price
Green | Round | 2 | 1
Red | Round | 4 | 1.3
Green | Rectangular | 6 | 1.3
Green | Rectangular | 9 | 1.4
Red | Round | 7 | 1.75
Blue | Rectangular | 12 | 2
>>> marbles.sort(3, distinct = True)
Color | Shape | Amount | Price
Green | Round | 2 | 1
Red | Round | 4 | 1.3
Green | Rectangular | 9 | 1.4
Red | Round | 7 | 1.75
Blue | Rectangular | 12 | 2
```
在`help`文本的最上面,出现了`sort`方法的签名。
```py
sort(column_or_label, descending=False, distinct=False)
```
这描述了`sort`的三个参数的位置,名称和默认值。 调用此方法时,可以使用位置参数或具名参数,因此以下三个调用完全相同。
```py
sort('SALARY', True)
sort('SALARY', descending=True)
sort(column_or_label='SALARY', descending=True)
```
当一个参数只是`True`或`False`时,包含参数名称是实用的约定,以便更明显地说明参数值的含义。
## 行的选取
通常,我们只想提取那些行,它们对应具有特定特征的条目。 例如,我们可能只需要对应勇士的行,或者获得超过一千万美元的球员。 或者我们可能只想要薪水前五名的人。
### 指定行
`Table`的方法就是干这个的 - 它需要一组指定的行。 它的参数是行索引或索引数组,它创建一个只包含这些行的新表。
例如,如果我们只想要`nba`的第一行,我们可以这样使用`take`。
```py
nba
```
| `PLAYER` | `POSITION` | `TEAM` | `'15-'16 SALARY` |
| --- | --- | --- | --- |
| Paul Millsap | PF | Atlanta Hawks | 18.6717 |
| Al Horford | C | Atlanta Hawks | 12 |
| Tiago Splitter | C | Atlanta Hawks | 9.75625 |
| Jeff Teague | PG | Atlanta Hawks | 8 |
| Kyle Korver | SG | Atlanta Hawks | 5.74648 |
| Thabo Sefolosha | SF | Atlanta Hawks | 4 |
| Mike Scott | PF | Atlanta Hawks | 3.33333 |
| Kent Bazemore | SF | Atlanta Hawks | 2 |
| Dennis Schroder | PG | Atlanta Hawks | 1.7634 |
| Tim Hardaway Jr. | SG | Atlanta Hawks | 1.30452 |
(省略了 407 行)
```py
nba.take(0)
```
| `PLAYER` | `POSITION` | `TEAM` | `'15-'16 SALARY` |
| --- | --- | --- | --- |
| Paul Millsap | PF | Atlanta Hawks | 18.6717 |
这是一个新表,只拥有我们指定的单个行。
通过指定一系列索引作为参数,我们还可以获得第四,第五和第六行。
```py
nba.take(np.arange(3, 6))
```
| `PLAYER` | `POSITION` | `TEAM` | `'15-'16 SALARY` |
| --- | --- | --- | --- |
| Jeff Teague | PG | Atlanta Hawks | 8 |
| Kyle Korver | SG | Atlanta Hawks | 5.74648 |
| Thabo Sefolosha | SF | Atlanta Hawks | 4 |
如果我们想要前五个最高薪球员的表格,我们可以先按薪水排序,然后取前五行:
```py
nba.sort('SALARY', descending=True).take(np.arange(5))
```
| `PLAYER` | `POSITION` | `TEAM` | `'15-'16 SALARY` |
| --- | --- | --- | --- |
| Kobe Bryant | SF | Los Angeles Lakers | 25 |
| Joe Johnson | SF | Brooklyn Nets | 24.8949 |
| LeBron James | SF | Cleveland Cavaliers | 22.9705 |
| Carmelo Anthony | SF | New York Knicks | 22.875 |
| Dwight Howard | C | Houston Rockets | 22.3594 |
### 对应指定特征的行
更常见的情况是,我们打算访问一组行中的数据,它们具有某种特征,但是我们并不知道其索引。 例如,我们可能想要所有薪水大于一千万美元的球员的数据,但我们不希望花费时间,对已排序的表中的行进行计数。
`where`方法可以做到。 它的输出是一个表格,列与原始表格相同,但只有特征出现的行。
`where`的第一个参数是列标签,列中包含信息,有关某行是否具有我们想要的特征。 如果特征是“薪水超过一千万美元”,那么列就是`SALARY`。
`where`的第二个参数是用于指定特征的方式。 一些例子会使指定的一般方式更容易理解。
在第一个例子中,我们提取了所有薪水超过一千万美元的人的数据。
```py
nba.where('SALARY', are.above(10))
```
| `PLAYER` | `POSITION` | `TEAM` | `'15-'16 SALARY` |
| --- | --- | --- | --- |
| Paul Millsap | PF | Atlanta Hawks | 18.6717 |
| Al Horford | C | Atlanta Hawks | 12 |
| Joe Johnson | SF | Brooklyn Nets | 24.8949 |
| Thaddeus Young | PF | Brooklyn Nets | 11.236 |
| Al Jefferson | C | Charlotte Hornets | 13.5 |
| Nicolas Batum | SG | Charlotte Hornets | 13.1253 |
| Kemba Walker | PG | Charlotte Hornets | 12 |
| Derrick Rose | PG | Chicago Bulls | 20.0931 |
| Jimmy Butler | SG | Chicago Bulls | 16.4075 |
| Joakim Noah | C | Chicago Bulls | 13.4 |
(省略了 59 行)
`are.above(10)`的参数的确保了,每个选择的行的`SALARY`大于 10。
新的表格有 69 行,相当于 69 个球员的薪水是一千万美元。 按顺序排列这些行使数据更易于分析。 多伦多猛龙队(Toronto Raptors)的德玛尔·德罗赞(DeMar DeRozan)是这个分组(薪水超过一千万美元)中“最穷”的一个。
```py
nba.where('SALARY', are.above(10)).sort('SALARY')
```
| `PLAYER` | `POSITION` | `TEAM` | `'15-'16 SALARY` |
| --- | --- | --- | --- |
| DeMar DeRozan | SG | Toronto Raptors | 10.05 |
| Gerald Wallace | SF | Philadelphia 76ers | 10.1059 |
| Luol Deng | SF | Miami Heat | 10.1516 |
| Monta Ellis | SG | Indiana Pacers | 10.3 |
| Wilson Chandler | SF | Denver Nuggets | 10.4494 |
| Brendan Haywood | C | Cleveland Cavaliers | 10.5225 |
| Jrue Holiday | PG | New Orleans Pelicans | 10.5955 |
| Tyreke Evans | SG | New Orleans Pelicans | 10.7346 |
| Marcin Gortat | C | Washington Wizards | 11.2174 |
| Thaddeus Young | PF | Brooklyn Nets | 11.236 |
(省略了 59 行)
斯蒂芬·库里(Stephen Curry)挣了多少? 对于答案,我们必须访问`PLAYER`的值等于`Stephen Curry`的行。 这是一个只包含一行的表格:
```py
nba.where('PLAYER', are.equal_to('Stephen Curry'))
```
| `PLAYER` | `POSITION` | `TEAM` | `'15-'16 SALARY` |
| --- | --- | --- | --- |
| Stephen Curry | PG | Golden State Warriors | 11.3708 |
库里只有不到 1140 万美元。 这是很多钱,但还不到勒布朗·詹姆斯(LeBron James)薪水的一半。 你可以在本节前面的“前 5 名”表中找到薪水,或者你可以在上面的代码中找到它,将`'Stephen Curry`换成`'LeBron James'`。
代码中再次使用了`are`,但这次是谓词`equal_to`而不是上面那个。 因此,例如,你可以得到包含所有的勇士的表格:
```py
nba.where('TEAM', are.equal_to('Golden State Warriors')).show()
```
| `PLAYER` | `POSITION` | `TEAM` | `'15-'16 SALARY` |
| --- | --- | --- | --- |
| Klay Thompson | SG | Golden State Warriors | 15.501 |
| Draymond Green | PF | Golden State Warriors | 14.2609 |
| Andrew Bogut | C | Golden State Warriors | 13.8 |
| Andre Iguodala | SF | Golden State Warriors | 11.7105 |
| Stephen Curry | PG | Golden State Warriors | 11.3708 |
| Jason Thompson | PF | Golden State Warriors | 7.00847 |
| Shaun Livingston | PG | Golden State Warriors | 5.54373 |
| Harrison Barnes | SF | Golden State Warriors | 3.8734 |
| Marreese Speights | C | Golden State Warriors | 3.815 |
| Leandro Barbosa | SG | Golden State Warriors | 2.5 |
| Festus Ezeli | C | Golden State Warriors | 2.00875 |
| Brandon Rush | SF | Golden State Warriors | 1.27096 |
| Kevon Looney | SF | Golden State Warriors | 1.13196 |
| Anderson Varejao | PF | Golden State Warriors | 0.289755 |
这部分表格已经按薪水排序,因为原始的表格按薪水排序,列出了同一个球队中球员。 行尾的`.show()`确保显示所有行,而不仅仅是前 10 行。
请求某列等于某值的行非常普遍,因此`are.equal_to`调用是可选的。 相反,仅仅使用列名和值来调用`where`方法,以达到相同的效果。
```
nba.where('TEAM', 'Denver Nuggets')
# equivalent to nba.where('TEAM', are.equal_to('Denver Nuggets'))
```
| `PLAYER` | `POSITION` | `TEAM` | `'15-'16 SALARY` |
| --- | --- | --- | --- |
| Danilo Gallinari | SF | Denver Nuggets | 14 |
| Kenneth Faried | PF | Denver Nuggets | 11.236 |
| Wilson Chandler | SF | Denver Nuggets | 10.4494 |
| JJ Hickson | C | Denver Nuggets | 5.6135 |
| Jameer Nelson | PG | Denver Nuggets | 4.345 |
| Will Barton | SF | Denver Nuggets | 3.53333 |
| Emmanuel Mudiay | PG | Denver Nuggets | 3.10224 |
| Darrell Arthur | PF | Denver Nuggets | 2.814 |
| Jusuf Nurkic | C | Denver Nuggets | 1.842 |
| Joffrey Lauvergne | C | Denver Nuggets | 1.70972 |
(省略了 4 行)
### 多个属性
通过重复使用`where`,你可以访问具有多个指定特征的行。 例如,这是一种方法,提取薪水超过一千五百万美元的所有控球后卫。
```py
nba.where('POSITION', 'PG').where('SALARY', are.above(15))
```
| `PLAYER` | `POSITION` | `TEAM` | `'15-'16 SALARY` |
| --- | --- | --- | --- |
| Derrick Rose | PG | Chicago Bulls | 20.0931 |
| Kyrie Irving | PG | Cleveland Cavaliers | 16.4075 |
| Chris Paul | PG | Los Angeles Clippers | 21.4687 |
| Russell Westbrook | PG | Oklahoma City Thunder | 16.7442 |
| John Wall | PG | Washington Wizards | 15.852 |
### 一般形式
现在你已经意识到,通过选择具有给定特征的行,来创建新表的一般方法,是使用`where`和`are`,以及适当的条件:
```py
original_table_name.where(column_label_string, are.condition)
nba.where('SALARY', are.between(10, 10.3))
```
| `PLAYER` | `POSITION` | `TEAM` | `'15-'16 SALARY` |
| --- | --- | --- | --- |
| Luol Deng | SF | Miami Heat | 10.1516 |
| Gerald Wallace | SF | Philadelphia 76ers | 10.1059 |
| Danny Green | SG | San Antonio Spurs | 10 |
| DeMar DeRozan | SG | Toronto Raptors | 10.05 |
请注意,上面的表格包括赚一千万美元的 Danny Green,而不包括一千三百万美元的 Monta Ellis。 与 Python 中的其他地方一样,范围包括左端但不包括右端。
如果我们指定一个任何行都不满足的条件,我们得到一个带有列标签但没有行的表。
```py
nba.where('PLAYER', are.equal_to('Barack Obama'))
```
| PLAYER | POSITION | TEAM | SALARY |
| --- | --- | --- | --- |
| | | |
### 更多条件
这里有一些谓词,你可能会觉得有用。 请注意,`x`和`y`是数字,`STRING`是一个字符串,`Z`是数字或字符串;你必须指定这些,取决于你想要的特征。
| 谓词 | 描述 |
| --- | --- |
| `are.equal_to(Z)` | 等于`Z` |
| `are.above(x)` | 大于`x` |
| `are.above_or_equal_to(x)` | 大于等于`x` |
| `are.below(x)` | 小于`x` |
| `are.below_or_equal_to(x)` | 小于等于`x` |
| `are.between(x, y)` | 大于等于`x`,小于`y` |
| `are.strictly_between(x, y)` | 大于`x`,小于`y` |
| `are.between_or_equal_to(x, y)` | 大于等于`x`,小于等于`y` |
| `are.containing(S)` | 包含字符串`S` |
你也可以指定任何这些条件的否定,通过在条件前面使用`.not_`。
| 谓词 | 描述 |
| --- | --- |
| `are.not_equal_to(Z)` | 不等于`Z` |
| `are.not_above(x)` | 不大于`x` |
以及其他。通常的逻辑规则是适用的,例如,“不大于`x`”等价于“小于等于`x`”。
我们以一系列示例结束这一节。
`are.containing`的使用有助于少打一些字。 例如,你可以指定`Warriors`而不是`Golden State Warriors`:
```py
nba.where('TEAM', are.containing('Warriors')).show()
```
| `PLAYER` | `POSITION` | `TEAM` | `'15-'16 SALARY` |
| --- | --- | --- | --- |
| Klay Thompson | SG | Golden State Warriors | 15.501 |
| Draymond Green | PF | Golden State Warriors | 14.2609 |
| Andrew Bogut | C | Golden State Warriors | 13.8 |
| Andre Iguodala | SF | Golden State Warriors | 11.7105 |
| Stephen Curry | PG | Golden State Warriors | 11.3708 |
| Jason Thompson | PF | Golden State Warriors | 7.00847 |
| Shaun Livingston | PG | Golden State Warriors | 5.54373 |
| Harrison Barnes | SF | Golden State Warriors | 3.8734 |
| Marreese Speights | C | Golden State Warriors | 3.815 |
| Leandro Barbosa | SG | Golden State Warriors | 2.5 |
| Festus Ezeli | C | Golden State Warriors | 2.00875 |
| Brandon Rush | SF | Golden State Warriors | 1.27096 |
| Kevon Looney | SF | Golden State Warriors | 1.13196 |
| Anderson Varejao | PF | Golden State Warriors | 0.289755 |
你可以提取所有后卫的数据,包括控球后卫和得分后卫。
```py
nba.where('POSITION', are.containing('G'))
```
| `PLAYER` | `POSITION` | `TEAM` | `'15-'16 SALARY` |
| --- | --- | --- | --- |
| Jeff Teague | PG | Atlanta Hawks | 8 |
| Kyle Korver | SG | Atlanta Hawks | 5.74648 |
| Dennis Schroder | PG | Atlanta Hawks | 1.7634 |
| Tim Hardaway Jr. | SG | Atlanta Hawks | 1.30452 |
| Jason Richardson | SG | Atlanta Hawks | 0.947276 |
| Lamar Patterson | SG | Atlanta Hawks | 0.525093 |
| Terran Petteway | SG | Atlanta Hawks | 0.525093 |
| Avery Bradley | PG | Boston Celtics | 7.73034 |
| Isaiah Thomas | PG | Boston Celtics | 6.91287 |
| Marcus Smart | PG | Boston Celtics | 3.43104 |
(省略了 171 行)
你可以获取所有不是克利夫兰骑士队的球员,并且薪水不低于两千万美元:
```py
other_than_Cavs = nba.where('TEAM', are.not_equal_to('Cleveland Cavaliers'))
other_than_Cavs.where('SALARY', are.not_below(20))
```
| `PLAYER` | `POSITION` | `TEAM` | `'15-'16 SALARY` |
| --- | --- | --- | --- |
| Joe Johnson | SF | Brooklyn Nets | 24.8949 |
| Derrick Rose | PG | Chicago Bulls | 20.0931 |
| Dwight Howard | C | Houston Rockets | 22.3594 |
| Chris Paul | PG | Los Angeles Clippers | 21.4687 |
| Kobe Bryant | SF | Los Angeles Lakers | 25 |
| Chris Bosh | PF | Miami Heat | 22.1927 |
| Dwyane Wade | SG | Miami Heat | 20 |
| Carmelo Anthony | SF | New York Knicks | 22.875 |
| Kevin Durant | SF | Oklahoma City Thunder | 20.1586 |
有很多方式可以创建相同的表格。这里是另一种,并且显然你可以想出来更多。
```py
other_than_Cavs.where('SALARY', are.above_or_equal_to(20))
```
| `PLAYER` | `POSITION` | `TEAM` | `'15-'16 SALARY` |
| --- | --- | --- | --- |
| Joe Johnson | SF | Brooklyn Nets | 24.8949 |
| Derrick Rose | PG | Chicago Bulls | 20.0931 |
| Dwight Howard | C | Houston Rockets | 22.3594 |
| Chris Paul | PG | Los Angeles Clippers | 21.4687 |
| Kobe Bryant | SF | Los Angeles Lakers | 25 |
| Chris Bosh | PF | Miami Heat | 22.1927 |
| Dwyane Wade | SG | Miami Heat | 20 |
| Carmelo Anthony | SF | New York Knicks | 22.875 |
| Kevin Durant | SF | Oklahoma City Thunder | 20.1586 |
你可以看到,`where`的使用提供了很大的灵活性,来访问你感兴趣的特征。 不要犹豫,尝试它吧!
## 示例:人口趋势
### 美国人口的趋势
现在我们做好了处理大量的数据表的准备。 下面的文件包含“美国居民人口的年度估计,按年龄和性别分列”。 请注意,`read_table`可以直接从 URL 读取数据。
```py
# As of Jan 2017, this census file is online here:
data = 'http://www2.census.gov/programs-surveys/popest/datasets/2010-2015/national/asrh/nc-est2015-agesex-res.csv'
# A local copy can be accessed here in case census.gov moves the file:
# data = 'nc-est2015-agesex-res.csv'
full_census_table = Table.read_table(data)
full_census_table
```
| SEX | AGE | CENSUS2010POP | ESTIMATESBASE2010 | POPESTIMATE2010 | POPESTIMATE2011 | POPESTIMATE2012 | POPESTIMATE2013 | POPESTIMATE2014 | POPESTIMATE2015 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 0 | 0 | 3944153 | 3944160 | 3951330 | 3963087 | 3926540 | 3931141 | 3949775 | 3978038 |
| 0 | 1 | 3978070 | 3978090 | 3957888 | 3966551 | 3977939 | 3942872 | 3949776 | 3968564 |
| 0 | 2 | 4096929 | 4096939 | 4090862 | 3971565 | 3980095 | 3992720 | 3959664 | 3966583 |
| 0 | 3 | 4119040 | 4119051 | 4111920 | 4102470 | 3983157 | 3992734 | 4007079 | 3974061 |
| 0 | 4 | 4063170 | 4063186 | 4077551 | 4122294 | 4112849 | 3994449 | 4005716 | 4020035 |
| 0 | 5 | 4056858 | 4056872 | 4064653 | 4087709 | 4132242 | 4123626 | 4006900 | 4018158 |
| 0 | 6 | 4066381 | 4066412 | 4073013 | 4074993 | 4097605 | 4142916 | 4135930 | 4019207 |
| 0 | 7 | 4030579 | 4030594 | 4043046 | 4083225 | 4084913 | 4108349 | 4155326 | 4148360 |
| 0 | 8 | 4046486 | 4046497 | 4025604 | 4053203 | 4093177 | 4095711 | 4120903 | 4167887 |
| 0 | 9 | 4148353 | 4148369 | 4125415 | 4035710 | 4063152 | 4104072 | 4108349 | 4133564 |
(已省略 296 行)
只显示了表格的前 10 行。稍后我们将看到如何显示整个表格;但是,这通常不适用于大型表格。
表格的描述一起出现。`SEX`列包含数字代码:`0`代表总体,`1代`表男性,`2`代表女性。 `AGE`列包含完整年份为单位的年龄,但特殊值`999`是人口的总和。 其余的列包含美国人口的估计。
通常,公共表格将包含更多的信息,不仅仅是特定调查或分析所需的信息。 在这种情况下,我们假设我们只对 2010 年到 2014 年的人口变化感兴趣。让我们选择相关的列。
```py
partial_census_table = full_census_table.select('SEX', 'AGE', 'POPESTIMATE2010', 'POPESTIMATE2014')
partial_census_table
```
| SEX | AGE | POPESTIMATE2010 | POPESTIMATE2014 |
| --- | --- | --- | --- |
| 0 | 0 | 3951330 | 3949775 |
| 0 | 1 | 3957888 | 3949776 |
| 0 | 2 | 4090862 | 3959664 |
| 0 | 3 | 4111920 | 4007079 |
| 0 | 4 | 4077551 | 4005716 |
| 0 | 5 | 4064653 | 4006900 |
| 0 | 6 | 4073013 | 4135930 |
| 0 | 7 | 4043046 | 4155326 |
| 0 | 8 | 4025604 | 4120903 |
| 0 | 9 | 4125415 | 4108349 |
(已省略 296 行)
我们也可以简化所选列的标签。
```py
us_pop = partial_census_table.relabeled('POPESTIMATE2010', '2010').relabeled('POPESTIMATE2014', '2014')
us_pop
```
| SEX | AGE | 2010 | 2014 |
| --- | --- | --- | --- |
| 0 | 0 | 3951330 | 3949775 |
| 0 | 1 | 3957888 | 3949776 |
| 0 | 2 | 4090862 | 3959664 |
| 0 | 3 | 4111920 | 4007079 |
| 0 | 4 | 4077551 | 4005716 |
| 0 | 5 | 4064653 | 4006900 |
| 0 | 6 | 4073013 | 4135930 |
| 0 | 7 | 4043046 | 4155326 |
| 0 | 8 | 4025604 | 4120903 |
| 0 | 9 | 4125415 | 4108349 |
(已省略 296 行)
我们现在有了一个易于使用的表格。 表中的每一列都是一个等长的数组,因此列可以使用算术进行组合。 这是 2010 年至 2014 年的人口变化。
```py
us_pop.column('2014') - us_pop.column('2010')
array([ -1555, -8112, -131198, ..., 6443, 12950, 4693244])
```
让我们使用包含这些变化的列来扩展`us_pop`,一列是绝对数值,另一列是相对于 2010 年数值的百分比。
```py
change = us_pop.column('2014') - us_pop.column('2010')
census = us_pop.with_columns(
'Change', change,
'Percent Change', change/us_pop.column('2010')
)
census.set_format('Percent Change', PercentFormatter)
```
| SEX | AGE | 2010 | 2014 | Change | Percent Change |
| --- | --- | --- | --- | --- | --- |
| 0 | 0 | 3951330 | 3949775 | -1555 | -0.04% |
| 0 | 1 | 3957888 | 3949776 | -8112 | -0.20% |
| 0 | 2 | 4090862 | 3959664 | -131198 | -3.21% |
| 0 | 3 | 4111920 | 4007079 | -104841 | -2.55% |
| 0 | 4 | 4077551 | 4005716 | -71835 | -1.76% |
| 0 | 5 | 4064653 | 4006900 | -57753 | -1.42% |
| 0 | 6 | 4073013 | 4135930 | 62917 | 1.54% |
| 0 | 7 | 4043046 | 4155326 | 112280 | 2.78% |
| 0 | 8 | 4025604 | 4120903 | 95299 | 2.37% |
| 0 | 9 | 4125415 | 4108349 | -17066 | -0.41% |
(已省略 296 行)
将数据排序。让我们按照人口绝对变化的降序排序表格。
```py
census.sort('Change', descending=True)
```
| SEX | AGE | 2010 | 2014 | Change | Percent Change |
| --- | --- | --- | --- | --- | --- |
| 0 | 999 | 309346863 | 318907401 | 9560538 | 3.09% |
| 1 | 999 | 152088043 | 156955337 | 4867294 | 3.20% |
| 2 | 999 | 157258820 | 161952064 | 4693244 | 2.98% |
| 0 | 67 | 2693707 | 3485241 | 791534 | 29.38% |
| 0 | 64 | 2706055 | 3487559 | 781504 | 28.88% |
| 0 | 66 | 2621335 | 3347060 | 725725 | 27.69% |
| 0 | 65 | 2678525 | 3382824 | 704299 | 26.29% |
| 0 | 71 | 1953607 | 2519705 | 566098 | 28.98% |
| 0 | 34 | 3822189 | 4364748 | 542559 | 14.19% |
| 0 | 23 | 4217228 | 4702156 | 484928 | 11.50% |
毫不奇怪,排序后表格的第一行对应整个人口:所有年龄和性别的分组。 从 2010 年到 2014 年,美国人口增加了约 950 万人,仅为 3%。
接下来的两行分别对应所有的男性和所有的女性。 以绝对数量和百分比来衡量,男性人口的增长高于女性人口。 百分比变化都在 3% 左右。
现在看看接下来的几行。 百分比变化从总人口的 3% 左右,上升到 60 年代末和 70 年代初的近 30%。 这个惊人的变化称为美国的老龄化。
到目前为止,2014 年 64~67 岁年龄段的绝对变化最大。什么可以解释这一大幅增长的原因? 我们可以通过考察相关分组的出生年份,来探索这个问题。
那些 2010 年在 64~67 岁年龄段的人,在 1943 年到 1946 年间出生。珍珠港的袭击是在 1941 年底,美军在 1942 年发动了一场大规模战争,结束于 1945 年。
2014 年 64 岁到 67 岁的人生于 1947 年到 1950 年,是美国二战后的生育高峰。
战后的生育高峰,是我们观察到的巨大变化的主要原因。
## 示例:性别趋势
### 性别比例的趋势
我们现在拥有了足够的编码技能,足以检查美国人群中的特征和趋势。在这个例子中,我们将查看不同年龄的男性和女性的分布情况。我们将继续使用上一节中的`us_pop`表。
```py
us_pop
```
| SEX | AGE | 2010 | 2014 |
| --- | --- | --- | --- |
| 0 | 0 | 3951330 | 3949775 |
| 0 | 1 | 3957888 | 3949776 |
| 0 | 2 | 4090862 | 3959664 |
| 0 | 3 | 4111920 | 4007079 |
| 0 | 4 | 4077551 | 4005716 |
| 0 | 5 | 4064653 | 4006900 |
| 0 | 6 | 4073013 | 4135930 |
| 0 | 7 | 4043046 | 4155326 |
| 0 | 8 | 4025604 | 4120903 |
| 0 | 9 | 4125415 | 4108349 |
(已省略 296 行)
我们从之前对这个数据集的检查得知,表格的描述一起出现。 它提醒了表中包含的内容。
每一行表示一个年龄。`SEX`列包含数字代码:`0`代表总数,`1`代表男性,`2`代表女性。 年龄栏包含以完整年份为单位的年龄,但特殊值`999`代表整个人口,不论年龄是什么。其余的列包含美国人口的估计。
### 理解`AGE=100`
作为一个初步的例子,我们来解释一下表中最后的年龄的数据,其中年龄是`100`岁。下面的代码提取了男性和女性(性别代码`0`)的组合分组,年龄最大的行。
```py
us_pop.where('SEX', are.equal_to(0)).where('AGE', are.between(97, 101))
```
| SEX | AGE | 2010 | 2014 |
| --- | --- | --- | --- |
| 0 | 97 | 68893 | 83089 |
| 0 | 98 | 47037 | 59726 |
| 0 | 99 | 32178 | 41468 |
| 0 | 100 | 54410 | 71626 |
不足为奇的是,年龄越大,人数越少,例如 99 岁的人数少于 98 岁。
然而,令人吃惊的是,100 岁的数值比 99 岁的数值要大得多。仔细查看文件,这是因为人口普查局使用 100 作为 100 或岁以上的每个人的代码。
年龄为 100 岁的人不仅仅代表 100 岁的人,还包括年龄在 100 岁以上的人。这就是为什么那一行的数字大于 99 岁的人的数字。
### 男性和女性的整体比例
我们现在开始考察 2014 年的性别比例。首先,我们一起来看看所有的年龄。 请记住,这意味着查看`AGE`编码为`999`的行。`all_ages`表包含此信息。其中有三行:一个是两种性别总体,一个是男性(`SEX`代码为`1`),一个是女性(`SEX`代码为`2`)。
```py
us_pop_2014 = us_pop.drop('2010')
all_ages = us_pop_2014.where('AGE', are.equal_to(999))
all_ages
```
| SEX | AGE | 2014 |
| --- | --- | --- |
| 0 | 999 | 318907401 |
| 1 | 999 | 156955337 |
| 2 | 999 | 161952064 |
`all_ages`的第 0 行包含两年中每年的美国总人口。2014 年,美国人口刚刚少于 3.19 亿。
第 1 行包含男性的计数,女性是第 2 行。 比较这两行可以看到,在 2014 年,美国的女性比男性多。
第 1 行和第 2 行的人口数加起来为第 0 行的总人口数。
为了与其他数量进行比较,我们需要将这些数字转换为总人口中的百分比。 让我们访问 2014 年的总数并命名。 然后,我们将显示带有比例列的人口表格。 与我们先前观察到的,女性人数多于男性的情况一致,2014 年约有 50.8% 的人口是女性,两年的每年中,约有 49.2% 的人口是男性。
```py
pop_2014 = all_ages.column('2014').item(0)
all_ages.with_column(
'Proportion', all_ages.column('2014')/pop_2014
).set_format('Proportion', PercentFormatter)
```
| SEX | AGE | 2014 | Proportion |
| --- | --- | --- | --- |
| 0 | 999 | 318907401 | 100.00% |
| 1 | 999 | 156955337 | 49.22% |
| 2 | 999 | 161952064 | 50.78% |
### 新生儿中男孩和女孩的比例
但是,当我们查看婴儿时,情况正好相反。 让我们将婴儿定义为还没有完整一年的人,对应年龄为 0 的行。这里是他们的人口数量。 你可以看到男婴比女婴多。
```py
infants = us_pop_2014.where('AGE', are.equal_to(0))
infants
```
| SEX | AGE | 2014 |
| --- | --- | --- |
| 0 | 0 | 3949775 |
| 1 | 0 | 2020326 |
| 2 | 0 | 1929449 |
像以前一样,我们可以将这些数字转换成婴儿总数中的百分比。 所得表格显示,2014 年,美国超过 51% 的婴儿是男性。
```py
infants_2014 = infants.column('2014').item(0)
infants.with_column(
'Proportion', infants.column('2014')/infants_2014
).set_format('Proportion', PercentFormatter)
```
| SEX | AGE | 2014 | Proportion |
| --- | --- | --- | --- |
| 0 | 0 | 3949775 | 100.00% |
| 1 | 0 | 2020326 | 51.15% |
| 2 | 0 | 1929449 | 48.85% |
事实上,长期以来,新生儿中男孩的比例略高于 1/2。 这个原因还没有得到彻底的理解,[科学家们还在努力](http://www.npr.org/sections/health-shots/2015/03/30/396384911/why-are-more-baby-boys-born-than-girls)。
### 每个年龄的男女比例
我们已经看到,虽然男婴比女婴多,但女性总数比男性多。 所以很显然,性别之间的分隔在不同年龄之间必须有所不同。
为了研究这个变化,我们将女性和男性的数据分开,并消除所有年龄的组合,年龄编码为 999 的行。
`females`和`male`表格分别包含两个性别的数据。
```py
females_all_rows = us_pop_2014.where('SEX', are.equal_to(2))
females = females_all_rows.where('AGE', are.not_equal_to(999))
females
```
| SEX | AGE | 2014 |
| --- | --- | --- |
| 2 | 0 | 1929449 |
| 2 | 1 | 1931375 |
| 2 | 2 | 1935991 |
| 2 | 3 | 1957483 |
| 2 | 4 | 1961199 |
| 2 | 5 | 1962561 |
| 2 | 6 | 2024870 |
| 2 | 7 | 2032494 |
| 2 | 8 | 2015285 |
| 2 | 9 | 2010659 |
(省略了 91 行)
```py
males_all_rows = us_pop_2014.where('SEX', are.equal_to(1))
males = males_all_rows.where('AGE', are.not_equal_to(999))
males
```
| SEX | AGE | 2014 |
| --- | --- | --- |
| 1 | 0 | 2020326 |
| 1 | 1 | 2018401 |
| 1 | 2 | 2023673 |
| 1 | 3 | 2049596 |
| 1 | 4 | 2044517 |
| 1 | 5 | 2044339 |
| 1 | 6 | 2111060 |
| 1 | 7 | 2122832 |
| 1 | 8 | 2105618 |
| 1 | 9 | 2097690 |
(省略了 91 行)
现在的计划是,比较两年中每一年的,每个年龄的女性人数和男性人数。 数组和表格的方法为我们提供了直接的方式。 这两个表格中,每个年龄都有一行。
```py
males.column('AGE')
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
91, 92, 93, 94, 95, 96, 97, 98, 99, 100])
females.column('AGE')
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
91, 92, 93, 94, 95, 96, 97, 98, 99, 100])
```
对于任何特定年龄,我们都可以通过将女性人数除以男性人数,获的女性:男性性别比例。 为了一步完成它,我们可以使用列来提取女性数量的数组,和相应的男性数量的数组,然后简单地将一个数组除以另一个数组。 逐元素相除将为所有年份创建性别比例的数组。
```py
ratios = Table().with_columns(
'AGE', females.column('AGE'),
'2014 F:M RATIO', females.column('2014')/males.column('2014')
)
ratios
```
| AGE | 2014 F:M RATIO |
| --- | --- |
| 0 | 0.955019 |
| 1 | 0.956884 |
| 2 | 0.956672 |
| 3 | 0.955058 |
| 4 | 0.959248 |
| 5 | 0.959998 |
| 6 | 0.959172 |
| 7 | 0.957445 |
| 8 | 0.957099 |
| 9 | 0.958511 |
(省略了 91 行)
从输出中可以看出,九岁以下儿童的比例都在 0.96 左右。 当男女比例小于 1 时,女性比男性少。 因此,我们所看到的是,在 0~9 岁的年龄段中,女孩比男孩少。此外,在每个年龄中,每 100 个男孩大约对应 96 个女孩。
那么人口中女性的整体比例为什么高于男性呢?
当我们检查年龄的另一端时,会发现一些非同寻常的事情。 以下是 75 岁以上的男女比例。
```py
ratios.where('AGE', are.above(75)).show()
```
| AGE | 2014 F:M RATIO |
| --- | --- |
| 76 | 1.23487 |
| 77 | 1.25797 |
| 78 | 1.28244 |
| 79 | 1.31627 |
| 80 | 1.34138 |
| 81 | 1.37967 |
| 82 | 1.41932 |
| 83 | 1.46552 |
| 84 | 1.52048 |
| 85 | 1.5756 |
| 86 | 1.65096 |
| 87 | 1.72172 |
| 88 | 1.81223 |
| 89 | 1.91837 |
| 90 | 2.01263 |
| 91 | 2.09488 |
| 92 | 2.2299 |
| 93 | 2.33359 |
| 94 | 2.52285 |
| 95 | 2.67253 |
| 96 | 2.87998 |
| 97 | 3.09104 |
| 98 | 3.41826 |
| 99 | 3.63278 |
| 100 | 4.25966 |
不仅所有这些比例大于 1,在所有这些年龄段中,女性比男性多,其中许多比例大于 1。
+ 在 89 岁和 90 岁中,比例接近 2,这意味着 2014 年这些年龄的女性约为男性的两倍。
+ 在 98 岁和 99 岁中,女性约为男性的 3.5 至 4 倍。
如果你想知道有多少高龄的人,你可以使用 Python 来发现:
```py
males.where('AGE', are.between(98, 100))
```
| SEX | AGE | 2014 |
| --- | --- | --- |
| 1 | 98 | 13518 |
| 1 | 99 | 8951 |
```
females.where('AGE', are.between(98, 100))
```
| SEX | AGE | 2014 |
| --- | --- | --- |
| 2 | 98 | 46208 |
| 2 | 99 | 32517 |
下图展示了年龄相关的性别比率。 蓝色曲线显示 2014 年的比例与年龄。
从 0 岁到 60 岁,这个比例差不多是 1(表示男性和女性差不多相等),但从 65 岁开始,比例开始急剧上升(女性多于男性)。
美国女性人数多于男性,部分原因是老年妇女的显着的性别不平衡。
```py
ratios.plot('AGE')
```
![](https://box.kancloud.cn/809831fdd201da2ab0aac50c31960e6f_566x375.png)