Group By, Having & Count

介绍

既然你可以选择原始数据,你已经准备好学习如何对数据进行分组并在这些组中计数。这可以帮助你解决以下类似问题:

  • 我们商店每种水果卖了多少?
  • 兽医诊所治疗过多少种类的动物?

想要要做到这一点,你将学习这三个新技巧:GROUP BY ,HAVINGCOUNT()。再一次,我们将使用这个虚构的关于宠物的信息表。

COUNT()

COUNT(),顾名思义,返回一些列的数。如果将列的名称传递给它,它将返回该列中的条目数。

例如,如果我们在pets表中选择**ID列的COUNT()**,它将返回4,因为表中有4个ID.

COUNT()是一个聚合函数的一个例子,它接受许多值并返回一个值。(聚合函数的其他示例包括SUM(), AVG(), MIN(), and MAX())正如你在上面的图中所注意到的,聚合函数引入了奇怪的列名(如f0__)。在本教程的后面,你将学习如何将名称更改为更具描述性的名称。

GROUP BY

GROUP BY接受一个或多个列的名称,当应用COUNT()之类的聚合函数时,将该列中具有相同值的所有行视为一个单独的组。

例如,如果我们想知道在pets表中每种动物的数量,我们可以使用GROUP BYAnimal列中具有相同的值的行归为一类放在一起,通过使用COUNT()找出多少ID在每一个组中。

它返回一个包含三行的表(每一行对应一个不同的动物)。我们可以看到pets表包含1只兔子、1只狗和2只猫。

GROUP BY … HAVING

HAVINGGROUP BY结合使用,用于忽略不满足特定条件的组。所以这种查询,例如,将只包含多个ID的组。由于只有一个组满足指定的条件,查询将返回一个只有一行的表。

例子:哪条黑客新闻评论引发的讨论最多?

准备好查看真实数据集中的示例了马?黑客新闻数据集中包含来自黑客新闻社交网站的故事和评论信息。

我们将使用comments表,首先打印前几行。(我们已经隐藏了相应的代码。来看一眼,点击下面的Code按钮。)

In [1]

  1. from google.cloud import bigquery
  2. # Create a "Client" object
  3. client = bigquery.Client()
  4. # Construct a reference to the "hacker_news" dataset
  5. dataset_ref = client.dataset("hacker_news", project="bigquery-public-data")
  6. # API request - fetch the dataset
  7. dataset = client.get_dataset(dataset_ref)
  8. # Construct a reference to the "comments" table
  9. table_ref = dataset_ref.table("comments")
  10. # API request - fetch the table
  11. table = client.get_table(table_ref)
  12. # Preview the first five lines of the "comments" table
  13. client.list_rows(table, max_results=5).to_dataframe()
  1. Using Kaggle's public dataset BigQuery integration.
id by author time time_ts text parent deleted dead ranking
0 2701393 5l 5l 1309184881 2011-06-27 14:28:01+00:00 And the glazier who fixed all the broken windo… 2701243 None None 0
1 5811403 99 99 1370234048 2013-06-03 04:34:08+00:00 Does canada have the equivalent of H1B/Green c… 5804452 None None 0
2 21623 AF AF 1178992400 2007-05-12 17:53:20+00:00 Speaking of Rails, there are other options in … 21611 None None 0
3 10159727 EA EA 1441206574 2015-09-02 15:09:34+00:00 Humans and large livestock (and maybe even pet… 10159396 None None 0
4 2988424 Iv Iv 1315853580 2011-09-12 18:53:00+00:00 I must say I reacted in the same way when I re… 2988179 None None 0

Out [1]

输出输出输出输出

让我们使用这个表来查看哪些评论产生了最多的回复。即:

  • parent列表示被回复的评论

  • id列有唯一的ID用于标识每个评论。

我们可以根据parent列进行分组,并计算相应的ID列,以便计算作为对特定注释的响应而做出的注释的数量。(这可能不会马上说得通———在这点浪费点时间确保一切都是清楚的)

此外,由于我们只对流行评论感兴趣,所以我们将查看超过10条回复的评论。因此,我们只返回ID大于10的组。

In [2]

  1. # Query to select comments that received more than 10 replies
  2. query_popular = """
  3. SELECT parent, COUNT(id)
  4. FROM `bigquery-public-data.hacker_news.comments`
  5. GROUP BY parent
  6. HAVING COUNT(id) > 10
  7. """

既然我们的查询已经准备好了,让我们运行它并将结果存储在一个熊猫 数据帧中。

In [3]

  1. # Set up the query (cancel the query if it would use too much of
  2. # your quota, with the limit set to 10 GB)
  3. safe_config = bigquery.QueryJobConfig(maximum_bytes_billed=10**10)
  4. query_job = client.query(query_popular, job_config=safe_config)
  5. # API request - run the query, and convert the results to a pandas DataFrame
  6. popular_comments = query_job.to_dataframe()
  7. # Print the first five rows of the DataFrame
  8. popular_comments.head()

Out [3]

parent f0_
0 1822253 174
1 9680982 77
2 6739074 41
3 6640430 62
4 2665090 61

popular_comments数据帧中的每一行对应一条接收超过10条的回复评论。例如,ID为801208的评论收到了56条回复。

混叠和其他改进

这里有几个提示让你的查询更好:

  • COUNT(id)产生的列称为f0__.这不是一个描述性很强的名字。在你指定相应的聚合之后,你可以通过添加AS NumPosts来更改名字。这叫做混叠,在 下一节课中会详细地介绍。
  • 如果你不确定要在 COUNT()中放入什么,你可以使用COUNT(1)来计算每个组中的行数。大多数人觉得它特别易读,因为我们知道它不关注其他列。与提供列名相比,它还扫描更少的数据(使其更快且使用更少的数据访问配额)。

使用这些技巧,我们可以重写我们的查询:

In [4]

  1. # Improved version of earlier query, now with aliasing & improved readability
  2. query_improved = """
  3. SELECT parent, COUNT(1) AS NumPosts
  4. FROM `bigquery-public-data.hacker_news.comments`
  5. GROUP BY parent
  6. HAVING COUNT(1) > 10
  7. """
  8. safe_config = bigquery.QueryJobConfig(maximum_bytes_billed=10**10)
  9. query_job = client.query(query_improved, job_config=safe_config)
  10. # API request - run the query, and convert the results to a pandas DataFrame
  11. improved_df = query_job.to_dataframe()
  12. # Print the first five rows of the DataFrame
  13. improved_df.head()

Out [4]

parent NumPosts
0 4684384 87
1 6584683 47
2 9616946 78
3 7750036 57
4 8185461 63

现在你有了想要的数据,它有描述性的名字。这是很好的风格。

使用GROUP BY的注意事项

注意,因为它告诉SQL应用聚合函数(像COUNT()),所以在没有聚合函数的情况下使用GROUP BY是没有意义的。同样地,如果你有任何GROUP BY子句,那么所有变量都必须传递给任意一个。

  1. 按命令分组
  2. 一个聚合函数

思考下面的查询:

In [5]

  1. query_good = """
  2. SELECT parent, COUNT(id)
  3. FROM `bigquery-public-data.hacker_news.comments`
  4. GROUP BY parent
  5. """

注意有两个变量:parentid变量。

  • parent通过命令传递给组(在GROUP BY parent中)
  • id传递给聚合函数(在COUNT(id)中)

In [6]

  1. query_bad = """
  2. SELECT author, parent, COUNT(id)
  3. FROM `bigquery-public-data.hacker_news.comments`
  4. GROUP BY parent
  5. """

如果发生此错误,你将得到错误消息SELECT list expression references column (column’s name) which is neither grouped nor aggregated at

Your turn

这些聚合使你可以编写更有趣地查询,用这些编码试着自己练习练习