如何将 bigrams 拆分为 n 列的列和行对
How to split bigrams into column and row pairs for n-columns
假设这样一个数据框:
# example dataset
df <- data.frame(
rowid = 1:3,
a = c("ax","cz","by"),
b = c("cy","ax","bz"),
c = c("bz","ay","cx")
)
实现以下转换的有效方法是什么?
#> # A tibble: 3 x 4
#> rowid a b c
#> <int> <chr> <chr> <chr>
#> 1 x z y
#> 2 x y z
#> 3 y z x
目标是获取每个双字母组的第二个字符,并将其排序到由第一个字符挑选出的列中,每一行。
如果可能,比较基础 R 和 Tidyverse 解决方案会很有用。
Tidyverse 解决方案的部分灵感来自于最近 post 使用 sjmisc
包中的 rotate_df()
:
df <- data.frame(
rowid = 1:3,
a = c("ax","cz","by"),
b = c("cy","ax","bz"),
c = c("bz","ay","cx")
)
library(sjmisc)
df %>%
# transpose the dataframe keeping column names
rotate_df(cn=TRUE) %>%
# sort columns by first character
mutate(across(everything(),sort)) %>%
# transpose back
rotate_df() %>%
# remove first character from each string
mutate(across(everything(),~str_sub(.,2,-1))) %>%
# make `rowid` column
rownames_to_column(var="rowid")
可以选择使用 as_tibble()
将数据帧转换为 tibble 以完全匹配目标输出,给出:
#> # A tibble: 3 x 4
#> rowid a b c
#> <int> <chr> <chr> <chr>
#> 1 x z y
#> 2 x y z
#> 3 y z x
此解决方案将推广到 n-columns 并且 %>%
兼容。
由于您正在寻找 base 和 Tidyverse 的比较,我将提供一个 base 解决方案:
tdf <- t(df[-1])
tdf[] <- substr(tdf, 2, 2)[order(col(tdf), tdf)]
df[-1] <- t(tdf)
# rowid a b c
#1 1 x z y
#2 2 x y z
#3 3 y z x
解释这 3 个步骤:
.1) 复制一份t()
数据的转译版本
...
.2b) 使用此命令从每个字符串的第二个字母开始 select 并覆盖 <-
转置数据。
.3) t()
转回原始结构并覆盖<-
df
中的数据
30K 行基准
基地:
bigdf <- df[rep(1:3,10000),]
bigdf$rowid <- 1:30000
system.time({
tdf <- t(bigdf[-1])
tdf[] <- substr(tdf,2,2)[order(col(tdf), tdf)]
bigdf[-1] <- t(tdf)
})
## user system elapsed
## 0.023 0.000 0.023
整洁:
bigdf <- df[rep(1:3,10000),]
bigdf$rowid <- 1:30000
library(dplyr)
library(tibble)
library(sjmisc)
library(stringr)
system.time({
bigdf %>%
rotate_df(cn=TRUE) %>%
mutate(across(everything(),sort)) %>%
rotate_df() %>%
mutate(across(everything(),~str_sub(.,2,-1))) %>%
rownames_to_column(var="rowid")
})
## user system elapsed
## 21.177 0.047 21.244
假设这样一个数据框:
# example dataset
df <- data.frame(
rowid = 1:3,
a = c("ax","cz","by"),
b = c("cy","ax","bz"),
c = c("bz","ay","cx")
)
实现以下转换的有效方法是什么?
#> # A tibble: 3 x 4
#> rowid a b c
#> <int> <chr> <chr> <chr>
#> 1 x z y
#> 2 x y z
#> 3 y z x
目标是获取每个双字母组的第二个字符,并将其排序到由第一个字符挑选出的列中,每一行。
如果可能,比较基础 R 和 Tidyverse 解决方案会很有用。
Tidyverse 解决方案的部分灵感来自于最近 post 使用 sjmisc
包中的 rotate_df()
:
df <- data.frame(
rowid = 1:3,
a = c("ax","cz","by"),
b = c("cy","ax","bz"),
c = c("bz","ay","cx")
)
library(sjmisc)
df %>%
# transpose the dataframe keeping column names
rotate_df(cn=TRUE) %>%
# sort columns by first character
mutate(across(everything(),sort)) %>%
# transpose back
rotate_df() %>%
# remove first character from each string
mutate(across(everything(),~str_sub(.,2,-1))) %>%
# make `rowid` column
rownames_to_column(var="rowid")
可以选择使用 as_tibble()
将数据帧转换为 tibble 以完全匹配目标输出,给出:
#> # A tibble: 3 x 4
#> rowid a b c
#> <int> <chr> <chr> <chr>
#> 1 x z y
#> 2 x y z
#> 3 y z x
此解决方案将推广到 n-columns 并且 %>%
兼容。
由于您正在寻找 base 和 Tidyverse 的比较,我将提供一个 base 解决方案:
tdf <- t(df[-1])
tdf[] <- substr(tdf, 2, 2)[order(col(tdf), tdf)]
df[-1] <- t(tdf)
# rowid a b c
#1 1 x z y
#2 2 x y z
#3 3 y z x
解释这 3 个步骤:
.1) 复制一份t()
数据的转译版本
...
.2b) 使用此命令从每个字符串的第二个字母开始 select 并覆盖 <-
转置数据。
.3) t()
转回原始结构并覆盖<-
df
30K 行基准
基地:
bigdf <- df[rep(1:3,10000),]
bigdf$rowid <- 1:30000
system.time({
tdf <- t(bigdf[-1])
tdf[] <- substr(tdf,2,2)[order(col(tdf), tdf)]
bigdf[-1] <- t(tdf)
})
## user system elapsed
## 0.023 0.000 0.023
整洁:
bigdf <- df[rep(1:3,10000),]
bigdf$rowid <- 1:30000
library(dplyr)
library(tibble)
library(sjmisc)
library(stringr)
system.time({
bigdf %>%
rotate_df(cn=TRUE) %>%
mutate(across(everything(),sort)) %>%
rotate_df() %>%
mutate(across(everything(),~str_sub(.,2,-1))) %>%
rownames_to_column(var="rowid")
})
## user system elapsed
## 21.177 0.047 21.244