【问题标题】:Going from a list of elements to chemical formula从元素列表到化学式
【发布时间】:2019-03-23 06:12:53
【问题描述】:

我有一个元素组合列表,每个元素都在它自己的行中。有时这些元素为零。

   C H N O S
1  5 5 0 0 0
2  6 4 1 0 1
3  4 6 2 1 0

我需要将它们组合起来以便它们阅读,例如C5H5、C6H4NS、C4H6N2O。 这意味着对于任何值为“1”的元素,我应该只取列名,而对于任何值为 0 的元素,应该完全跳过该列。

我不确定从哪里开始。我可以添加一个新列,以便更轻松地跨列阅读,例如

   c C h H n N o O s S
1  C 5 H 5 N 0 O 0 S 0
2  C 6 H 4 N 1 O 0 S 1
3  C 4 H 6 N 2 O 1 S 0

这样,我只需要输出为单个字符串,但我需要忽略任何零值,并删除元素名称后面的一个。

【问题讨论】:

    标签: r dataframe data-manipulation chemistry


    【解决方案1】:

    这里有一个基本的 R 解决方案:

    df = read.table(text = "
    C H N O S
    5 5 0 0 0
    6 4 1 0 1
    4 6 2 1 0
    ", header=T)
    
    apply(df, 1, function(x){return(gsub('1', '', paste0(colnames(df)[x > 0], x[x > 0], collapse='')))})
    [1] "C5H5"    "C6H4NS"  "C4H6N2O"
    

    paste0(colnames(df)[x > 0], x[x > 0], collapse='') 将行值大于零的列名粘贴在一起。 gsub 然后删除那些。 apply 对数据框中的每一行都执行此操作。

    【讨论】:

    • 不错,走的是同一条路。顺便说一句,不需要return
    • @zx8754 没错,但我发现函数返回内容而不返回语句的整个想法有点可怕。
    【解决方案2】:

    这是一个 tidyverse 解决方案,它使用了一些重塑:

    df = read.table(text = "
    C H N O S
    5 5 0 0 0
    6 4 1 0 1
    4 6 2 1 0
    ", header=T)
    
    library(tidyverse)
    
    df %>%
      mutate(id = row_number()) %>%                      # add row id
      gather(key, value, -id) %>%                        # reshape data
      filter(value != 0) %>%                             # remove any zero rows
      mutate(value = ifelse(value == 1, "", value)) %>%  # replace 1 with ""
      group_by(id) %>%                                   # for each row
      summarise(v = paste0(key, value, collapse = ""))   # create the string value
    
    # # A tibble: 3 x 2
    #      id v      
    #   <int> <chr>  
    # 1     1 C5H5   
    # 2     2 C6H4NS 
    # 3     3 C4H6N2O
    

    【讨论】:

      【解决方案3】:

      假设输入矩阵m 在最后的注释中可重现地给出——如果它是使用as.matrix 的数据框,则将其转换为矩阵。

      现在创建一个与m 形状相同的矩阵,只包含字母,所以现在lets 包含字母,m 包含数字。然后将字母和数字粘贴在一起,并将数字为零的单元格替换为空字符串。还要用字母替换数字为 1 的任何单元格。最后将每一行粘贴在一起。没有使用任何包,也没有使用循环或 *apply。

      lets <-  t(replace(t(m), TRUE, colnames(m)))
      mm <- paste0(lets, m)
      mm <- replace(mm, m == 0, "")
      mm <- ifelse(m == 1, lets, mm)
      do.call("paste0", as.data.frame(mm))
      ## [1] "C5H5"    "C6H4NS"  "C4H6N2O"
      

      注意

      假设可重现形式的输入矩阵m为:

      m <- matrix(c(5, 6, 4, 5, 4, 6, 0, 1, 2, 0, 0, 1, 0, 1, 0), 3, 5,
        dimnames = list(NULL, c("C", "H", "N", "O", "S")))
      

      【讨论】:

        【解决方案4】:

        另一个避免apply 边距为 1 的想法,

        gsub('1', '', sapply(split(df, 1:nrow(df)), function(i) 
                                         paste(paste0(names(i)[i != 0], i[i != 0]), collapse = '')))
        
        #        1         2         3 
        #   "C5H5"  "C6H4NS" "C4H6N2O"
        

        【讨论】:

          【解决方案5】:

          另一种选择

          library(dplyr)
          #Get indices of all non-zero numbers in the dataframe
          inds <- which(df!=0, arr.ind = TRUE)
          
          #Create a dataframe with row index, column index and value at that position
          vals <- data.frame(inds, val = df[inds])
          
          #For each row paste the name of the column and value together and then replace 1
          vals %>%
            group_by(row) %>%
            summarise(chemical = paste0(names(df)[col], val,collapse = "")) %>%
            mutate(chemical = gsub("[1]", "", chemical))
          
          #   row chemical
          #  <int> <chr>   
          #1     1 C5H5    
          #2     2 C6H4NS  
          #3     3 C4H6N2O 
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2017-08-13
            • 1970-01-01
            • 2015-07-16
            • 1970-01-01
            • 1970-01-01
            • 2023-03-12
            • 1970-01-01
            相关资源
            最近更新 更多