使用dplyr和/或tidyr组合/排序列

时间:2017-03-17 12:56:01

标签: r dplyr igraph tidyr

编辑:我在下面尝试了一个解决方案,但由于我需要将因子转换为字符并返回到因子,我会丢失一些重要信息。

有了这个表,我希望它可以从中排序,

From    To  count
A       B     2
A       C     1
C       A     3
B       C     1

到此,

  From To count
1    A  B     2
2    A  C     4
3    B  C     1

到目前为止,我看到两个选项,要么这样做:

df[1:2] <- t(apply(df[1:2], 1, sort))    
aggregate(count ~ From + To, df, sum)

这很慢,因为我正在使用9.000.000观察。或者只是将其转换为iGraph网络,并合并边缘。

g <- graph_from_data_frame(df, directed = TRUE, vertices = nodes)
g <- as.undirected(g, mode = "mutual", edge.attr.comb=list(weight = "sum"))

我遇到的两个问题是我提到的第一个选项实际上应该使用dplyr或者tidyr,但到目前为止我还无法弄清楚如何做到这一点。

网络/ igraph选项比“t(apply(”选项)更快,但我仍然需要将图形转换回data.table以进行进一步分析。

关于如何运行“t(apply(”选项与dplyr或tidyr?

)的任何想法

3 个答案:

答案 0 :(得分:3)

在基础R中,我们可以使用非公式界面将akrun的pminpmax建议与aggregate结合使用,如下所示:

aggregate(df$count, list(From=pmin(df$From, df$To), To=pmax(df$From, df$To)), sum)
  From To x
1    A  B 2
2    A  C 4
3    B  C 1

请注意,这要求df$Fromdf$To是字符向量,而不是因素。

<强>定时
此方法比使用apply更快,因为它不涉及转换为矩阵。使用下面的大数据集,有900万观测值,使用pminpmaxaggregate完成的时间在我的计算机上是14.5秒,而OP的方法是apply花了442.2秒或30倍。

数据

df <-
structure(list(From = c("A", "A", "C", "B"), To = c("B", "C", 
"A", "C"), count = c(2L, 1L, 3L, 1L)), .Names = c("From", "To", 
"count"), class = "data.frame", row.names = c(NA, -4L))

更大的样本数据

set.seed(1234)
df <- data.frame(From=sample(LETTERS, 9e6, replace=TRUE),
                 To=sample(LETTERS, 9e6, replace=TRUE),
                 count=sample(100, 9e6, replace=TRUE),
                 stringsAsFactors=FALSE)

答案 1 :(得分:2)

我们可以使用pmin/pmax。应该更快

library(dplyr)
df1 %>% 
    group_by(From1 = pmin(From, To), To = pmax(From, To)) %>% 
    summarise(count = sum(count)) %>%
    rename(From = From1)
#  From    To count
#  <chr> <chr> <int>
#1     A     B     2
#2     A     C     4
#3     B     C     1

答案 2 :(得分:0)

library(tidyverse)
cols_before_merge <- c("From", "To")
out_cols <- c("col_1", "col_2")

df <- tibble::tribble(
  ~From, ~To, ~count,
  "A", "B", 2,
  "A", "C", 1,
  "C", "A", 3,
  "B", "C", 1,
)

基于上述内容,我认为创建唯一键的方法是:

df_out <- df %>%
  dplyr::mutate(
    key = purrr::pmap_chr(
      list(From, To),
      ~ stringr::str_c(stringr::str_sort(c(...)), collapse = "_")
    )
  )

或者是使用tidy evaluation的更具编程性的方法:

merge_sort <- function(cols_values) {
  purrr::pmap_chr(
    cols_values,
    ~ stringr::str_c(stringr::str_sort(c(...)), collapse = "_")
  )
}

add_key <- function(cols) {
  # column names need to be evaluated using the dataframe as an environment
  cols_quosure <- rlang::enquo(cols)

  # column names should be symbols not strings
  cols_syms <- rlang::syms(cols)

  cols_values <- purrr::map(
    cols_syms,
    ~ rlang::eval_tidy(.x, rlang::quo_get_env(cols_quosure))
  )

  merge_sort(cols_values)
}



# Adding columns for key construction programmatically
df_out <- df %>%
  dplyr::mutate(key = add_key(cols_before_merge))

最后要进行计数并确保输出列是因子(因为akrun指出行排序前后的因子水平很容易会有所不同)。

df_out %>%
  dplyr::count(key, name = "count") %>%
  tidyr::separate(key, sep = "_", into = out_cols) %>%
  dplyr::mutate_at(out_cols, as.factor)