在 MySQL 内将数据重塑为宽
Reshape data long to wide in MySQL
我有一个 table,我想 pivot/reshape 从长到宽。
The long format has 3 columns:
zipcode, category, zipnum
'12345', 'A', '1'
'40348', 'A', '2'
'16132', 'B', '1'
'09428', 'B', '2'
'14818', 'B', '3'
'93182', 'C', '1'
zipnum 列是每个邮政编码的行号,按类别分组。所以 zipnum 对于一个类别中的每个邮政编码都是唯一的。我试图实现的宽格式有列类别,zipcode1,...,zipcodeN,其中 N 是整个 table 中 zipnum 的最大值。在这个例子中 N=3,但在我的实际数据集中 N 大约是 2000。
category, zipcode1, zipcode2, zipcode3
'A', '12345', '40348', NULL
'B', '16132', '09428', '14818'
'C', '93182', NULL, NULL
我对 SQL 比较陌生 - 我知道如何在 Python、R 等中很容易地做到这一点,但找不到用 [=20 做到这一点的方法=].坦率地说,我什至不知道从哪里开始。
我建议您将它们组合成一个逗号分隔的字符串:
select category,
group_concat(zip_code order by zipnum) as zip_codes
from t
group by category;
因为您的字符串会很长,您需要将 group_concat_max_len
设置为大于其默认值的值。
您为什么不希望将这些作为专栏?好吧,MySQL 在 table 中有 4,096 列的硬限制,我认为这个限制也适用于结果集。 The limit, though, is further complicated by the storage engine and other settings.。有足够的复杂性,我只是不想动态地创建可能导致数千列的查询。
您可能会发现 JSON 结构比“单纯的”字符串更方便。 MySQL 也支持。
我认为这是使用 SQL 不容易做到的事情。例如,在 PostgreSQL 中有一个单独的库用于执行此操作(参见 here)。
但是 dbplyr
在这里可能会有所帮助,可以直接使用或间接用于生成 SQL,您可以在该包之外使用它。我没有 MySQL,所以我在 PostgreSQL 和 SQLite 上测试了以下内容。我认为它也应该与 RMySQL
一起使用:
library(tibble)
library(tidyr)
library(DBI)
library(dplyr, warn.conflicts = FALSE)
df <- tibble::tribble(
~zipcode, ~category, ~zipnum,
'12345', 'A', '1',
'40348', 'A', '2',
'16132', 'B', '1',
'09428', 'B', '2',
'14818', 'B', '3',
'93182', 'C', '1')
pg <- dbConnect(RPostgres::Postgres())
df_pg <- copy_to(pg, df, overwrite = TRUE)
df_pg %>%
pivot_wider(names_from = "zipnum", values_from = "zipcode",
names_prefix = "zipcode")
#> # Source: lazy query [?? x 4]
#> # Database: postgres [iangow@/tmp:5432/crsp]
#> category zipcode3 zipcode2 zipcode1
#> <chr> <chr> <chr> <chr>
#> 1 A <NA> 40348 12345
#> 2 C <NA> <NA> 93182
#> 3 B 14818 09428 16132
df_pg %>%
pivot_wider(names_from = "zipnum", values_from = "zipcode",
names_prefix = "zipcode") %>%
show_query()
#> <SQL>
#> SELECT "category", MAX(CASE WHEN ("zipnum" = '3') THEN ("zipcode") END) AS "zipcode3", MAX(CASE WHEN ("zipnum" = '2') THEN ("zipcode") END) AS "zipcode2", MAX(CASE WHEN ("zipnum" = '1') THEN ("zipcode") END) AS "zipcode1"
#> FROM "df"
#> GROUP BY "category"
df_sqlite <- dbplyr::memdb_frame(df)
df_sqlite %>%
pivot_wider(names_from = "zipnum", values_from = "zipcode",
names_prefix = "zipcode")
#> # Source: lazy query [?? x 4]
#> # Database: sqlite 3.35.2 [:memory:]
#> category zipcode1 zipcode2 zipcode3
#> <chr> <chr> <chr> <chr>
#> 1 A 12345 40348 <NA>
#> 2 B 16132 09428 14818
#> 3 C 93182 <NA> <NA>
df_sqlite %>%
pivot_wider(names_from = "zipnum", values_from = "zipcode",
names_prefix = "zipcode") %>%
show_query()
#> <SQL>
#> SELECT `category`, MAX(CASE WHEN (`zipnum` = '1') THEN (`zipcode`) END) AS `zipcode1`, MAX(CASE WHEN (`zipnum` = '2') THEN (`zipcode`) END) AS `zipcode2`, MAX(CASE WHEN (`zipnum` = '3') THEN (`zipcode`) END) AS `zipcode3`
#> FROM `dbplyr_001`
#> GROUP BY `category`
由 reprex package (v1.0.0)
于 2021-04-04 创建
我有一个 table,我想 pivot/reshape 从长到宽。
The long format has 3 columns:
zipcode, category, zipnum
'12345', 'A', '1'
'40348', 'A', '2'
'16132', 'B', '1'
'09428', 'B', '2'
'14818', 'B', '3'
'93182', 'C', '1'
zipnum 列是每个邮政编码的行号,按类别分组。所以 zipnum 对于一个类别中的每个邮政编码都是唯一的。我试图实现的宽格式有列类别,zipcode1,...,zipcodeN,其中 N 是整个 table 中 zipnum 的最大值。在这个例子中 N=3,但在我的实际数据集中 N 大约是 2000。
category, zipcode1, zipcode2, zipcode3
'A', '12345', '40348', NULL
'B', '16132', '09428', '14818'
'C', '93182', NULL, NULL
我对 SQL 比较陌生 - 我知道如何在 Python、R 等中很容易地做到这一点,但找不到用 [=20 做到这一点的方法=].坦率地说,我什至不知道从哪里开始。
我建议您将它们组合成一个逗号分隔的字符串:
select category,
group_concat(zip_code order by zipnum) as zip_codes
from t
group by category;
因为您的字符串会很长,您需要将 group_concat_max_len
设置为大于其默认值的值。
您为什么不希望将这些作为专栏?好吧,MySQL 在 table 中有 4,096 列的硬限制,我认为这个限制也适用于结果集。 The limit, though, is further complicated by the storage engine and other settings.。有足够的复杂性,我只是不想动态地创建可能导致数千列的查询。
您可能会发现 JSON 结构比“单纯的”字符串更方便。 MySQL 也支持。
我认为这是使用 SQL 不容易做到的事情。例如,在 PostgreSQL 中有一个单独的库用于执行此操作(参见 here)。
但是 dbplyr
在这里可能会有所帮助,可以直接使用或间接用于生成 SQL,您可以在该包之外使用它。我没有 MySQL,所以我在 PostgreSQL 和 SQLite 上测试了以下内容。我认为它也应该与 RMySQL
一起使用:
library(tibble)
library(tidyr)
library(DBI)
library(dplyr, warn.conflicts = FALSE)
df <- tibble::tribble(
~zipcode, ~category, ~zipnum,
'12345', 'A', '1',
'40348', 'A', '2',
'16132', 'B', '1',
'09428', 'B', '2',
'14818', 'B', '3',
'93182', 'C', '1')
pg <- dbConnect(RPostgres::Postgres())
df_pg <- copy_to(pg, df, overwrite = TRUE)
df_pg %>%
pivot_wider(names_from = "zipnum", values_from = "zipcode",
names_prefix = "zipcode")
#> # Source: lazy query [?? x 4]
#> # Database: postgres [iangow@/tmp:5432/crsp]
#> category zipcode3 zipcode2 zipcode1
#> <chr> <chr> <chr> <chr>
#> 1 A <NA> 40348 12345
#> 2 C <NA> <NA> 93182
#> 3 B 14818 09428 16132
df_pg %>%
pivot_wider(names_from = "zipnum", values_from = "zipcode",
names_prefix = "zipcode") %>%
show_query()
#> <SQL>
#> SELECT "category", MAX(CASE WHEN ("zipnum" = '3') THEN ("zipcode") END) AS "zipcode3", MAX(CASE WHEN ("zipnum" = '2') THEN ("zipcode") END) AS "zipcode2", MAX(CASE WHEN ("zipnum" = '1') THEN ("zipcode") END) AS "zipcode1"
#> FROM "df"
#> GROUP BY "category"
df_sqlite <- dbplyr::memdb_frame(df)
df_sqlite %>%
pivot_wider(names_from = "zipnum", values_from = "zipcode",
names_prefix = "zipcode")
#> # Source: lazy query [?? x 4]
#> # Database: sqlite 3.35.2 [:memory:]
#> category zipcode1 zipcode2 zipcode3
#> <chr> <chr> <chr> <chr>
#> 1 A 12345 40348 <NA>
#> 2 B 16132 09428 14818
#> 3 C 93182 <NA> <NA>
df_sqlite %>%
pivot_wider(names_from = "zipnum", values_from = "zipcode",
names_prefix = "zipcode") %>%
show_query()
#> <SQL>
#> SELECT `category`, MAX(CASE WHEN (`zipnum` = '1') THEN (`zipcode`) END) AS `zipcode1`, MAX(CASE WHEN (`zipnum` = '2') THEN (`zipcode`) END) AS `zipcode2`, MAX(CASE WHEN (`zipnum` = '3') THEN (`zipcode`) END) AS `zipcode3`
#> FROM `dbplyr_001`
#> GROUP BY `category`
由 reprex package (v1.0.0)
于 2021-04-04 创建