【问题标题】:Dataframe processing数据帧处理
【发布时间】:2016-07-21 10:32:16
【问题描述】:

我有一个由Match <- read.table("Match.txt", sep="", fill =T, stringsAsFactors = FALSE, quote = "", header = F) 读取的数据框,看起来像这样:

> ab
           V1       V2  V3                       V4 V5    V6 V7    V8 V9               V10
1  Inspecting sequence  ID chr1:173244300-173244500       NA       NA                     
2   V$ATF3_Q6        |  19                      (-)  | 0.877  | 0.622  |    aagtccCATCAggg
3   V$ATF3_Q6        |  34                      (-)  | 0.788  | 0.655  |    agggaaCGACAcag
4   V$ATF3_Q6        | 102                      (+)  | 0.738  | 0.685  |    cccTGAGCttagga
5  V$CEBPB_01        |  24                      (+)  | 0.950  | 0.882  |    ccatcagGGAAGgg
72   V$YY1_01        | 117                      (+)  | 0.996  | 0.984  | acttCCCATcttttaag
73 Inspecting sequence  ID chr1:173244350-173244550       NA       NA                     
74  V$ATF3_Q6        |  52                      (+)  | 0.738  | 0.685  |    cccTGAGCttagga
75  V$ATF3_Q6        | 160                      (+)  | 0.862  | 0.687  |    gtcTGACCtggaga
76 V$CEBPB_01        |  57                      (+)  | 0.966  | 0.958  |    agcttagGAAACtt

它包含百万个这样的重复,其中第一行是:Inspecting sequence ID chr1:173244300-173244500,然后是上面可以看到的一些值。我想处理它,记住以下几点:

  1. 提取第一行,在:- 上断开它,这样我将得到三列,例如:chr1 173244300 173244500
  2. 第 4 列应包含 V1$Row2 第一个元素,在 $_ 上拆分,然后取第二个索引,即 ATF3,像这样我有 30 个确定的(让我们称之为名称)案例,在每种情况下都会观察到一些,而另一些则不会(1 个案例是从第 1 行到第 72 行,第二个是从第 73 行开始)。
  3. 如果该名称出现在 1 个案例中,则将值 B 分配给该列,否则将分配值 U

所以根据我的输入,我想得到以下输出:

chr     start       stop        ATF3  CEBPB  YY1    ..(All which appear e.g from row 1 to 72, ignoring duplicates)
chr1    173244300   173244500   B     B      B  
chr1    173244350   173244550   B     B      U

我想在标题中设置一个固定编号列(我知道它们是 32 个这样的名称),因此如果它们出现在一种情况下,将分配 B,否则将分配 U

如果有人能帮我做这件事,那将是一个很大的帮助。

这是此示例数据帧的输出:

> ab <- dput(Match[c(1:5,72:76), ])
structure(list(V1 = c("Inspecting", "V$ATF3_Q6", "V$ATF3_Q6", 
"V$ATF3_Q6", "V$CEBPB_01", "V$YY1_01", "Inspecting", "V$ATF3_Q6", 
"V$ATF3_Q6", "V$CEBPB_01"), V2 = c("sequence", "|", "|", "|", 
"|", "|", "sequence", "|", "|", "|"), V3 = c("ID", "19", "34", 
"102", "24", "117", "ID", "52", "160", "57"), V4 = c("chr1:173244300-173244500", 
"(-)", "(-)", "(+)", "(+)", "(+)", "chr1:173244350-173244550", 
"(+)", "(+)", "(+)"), V5 = c("", "|", "|", "|", "|", "|", "", 
"|", "|", "|"), V6 = c(NA, 0.877, 0.788, 0.738, 0.95, 0.996, 
NA, 0.738, 0.862, 0.966), V7 = c("", "|", "|", "|", "|", "|", 
"", "|", "|", "|"), V8 = c(NA, 0.622, 0.655, 0.685, 0.882, 0.984, 
NA, 0.685, 0.687, 0.958), V9 = c("", "|", "|", "|", "|", "|", 
"", "|", "|", "|"), V10 = c("", "aagtccCATCAggg", "agggaaCGACAcag", 
"cccTGAGCttagga", "ccatcagGGAAGgg", "acttCCCATcttttaag", "", 
"cccTGAGCttagga", "gtcTGACCtggaga", "agcttagGAAACtt")), .Names = c("V1", 
"V2", "V3", "V4", "V5", "V6", "V7", "V8", "V9", "V10"), row.names = c(1L, 
2L, 3L, 4L, 5L, 72L, 73L, 74L, 75L, 76L), class = "data.frame")

【问题讨论】:

    标签: r dataframe bioinformatics


    【解决方案1】:

    this question 中的输入文件设为/c/tmp.txt

    这个 awk 脚本保存为 SO-38563400.awk:

    BEGIN {
     OFS="\t" # Set the output separator
     i=0 # Just to init the counter and be sure to start at 1 later
    }
     {
     #print $0
     }
    /Inspecting sequence ID/ { # Changing sequence, initialize new entry with start and end
      split($4,arr,"[:-]") # split the string in fields, split on : and -
      seq[i++,"chr"]=arr[1] # Save the chr part and increase the sequence beforehand
      seq[i,"start"]=arr[2] # save the start date
      seq[i,"end"]=arr[3] # Save the end date
    }
    
    /V[$][^_]+_.*/ { # V line type,
      split($1,arr,"[$_]") # Split on $ and underscore
      seq[i,arr[2]]="B" # This has been seen, setting to B
      seq[i,"print"]=1
      names[arr[2]]++ # Save the name for output
      # (and count occurences, just for fun, well mainly because an int is cheaper to store)
      # Main reason is it allow a quicker access toa rray keys ant END block
    }
    
    END {
      head=sprintf("char%sstart%sstop",OFS,OFS,OFS)
      for (h in names) {
        head=sprintf("%s%s%s",head,OFS,h)
      }
      print(head)
      for (l=1; l<i; l++) { # loop over each line/sequence
        line=sprintf("%s%s%s%s%s",seq[l,"chr"],OFS,seq[l,"start"],OFS,seq[l,"end"])
        for (h in names) {
          if (seq[l,h]=="B") line=sprintf("%s%s%s",line,OFS,"B")
          else line=sprintf("%s%s%s",line,OFS,"U")
        }
        if (seq[l,"print"]) print line
      }
    }
    

    传递这个命令:

    awk -f SO-38563400.awk /c/tmp.txt > /c/Rtable.txt
    

    给予:

    $ cat /c/Rtable.txt
    char    start   stop    STAT3   ATF3    TEAD4   GATA3   JUND    HNF4A   FOXA2   MAX     CEBPB   SPI1    GABPA   CMYC    P300    E2F1    CTCF    ATF2
    chr22   16049850        16050050        B       B       U       B       U       B       B       U       U       U       U       U       B       B       U       B
    chr22   16049900        16050100        B       B       B       B       B       B       B       B       B       B       B       B       B       B       B       B
    

    然后读入 r:

    > x <- read.table("/c/Rtable.txt", sep="\t",  stringsAsFactors = FALSE, header=T)
    > x
    char    start     stop STAT3 ATF3 TEAD4 GATA3 JUND HNF4A FOXA2 MAX CEBPB SPI1 GABPA CMYC P300 E2F1 CTCF ATF2
    1 chr22 16049850 16050050     B    B     U     B    U     B     B   U     U    U     U    U    B    B    U    B
    2 chr22 16049900 16050100     B    B     B     B    B     B     B   B     B    B     B    B    B    B    B    B
    

    请忽略/c/路径的设置,这可以在windows或linux上运行,windows下有awk的端口,由于文件流的操作系统容量,我建议使用linux处理大文件。

    我们可以通过在打印结果之前不读取整个文件来节省更多的内存,但这需要一组固定的“名称”,但是你懒得自己提取名称,只是给我发了一堆条目, 练习留给你适应,在 BEGIN 块中制作列表,将其用作每个 seq 的条目,并在每个新的 seq 上打印前一个结果,然后再进行处理。

    我希望下次你能花一些时间来提出一个正确的问题,并且你会明白你必须为他人帮助你做出一些努力,特别是在一系列 cmet 要求你改进你的问题之后。

    【讨论】:

    • 我将开始研究解决方案,但首先要感谢您的时间。
    • @Newbie 不客气,我相信 R 并不是万能的解决方案,尤其是将奇怪的数据转换为可用的东西。但我持怀疑态度,这不是 XY 问题,试图解决可能以另一种方式完成的先前步骤引起的问题,如果没有全貌显然很难猜测,这不是批评,我假设你无法控制在入口文件上。
    • 感谢您的指导和解答。我已经在我的示例文件上进行了尝试,它运行良好,现在在应用到 6 GB 的原始文件时等待最后 10 分钟的结果。我想更改输出分隔符,我可以通过OFS='\t' 来完成,其次,是否可以删除每列中包含“U”的所有行(从第 4 列开始)?我不知道该怎么做。
    • 因此,在这种情况下,第 1:6 和第 9 行将被删除。我知道如何在 R 中执行此操作,但我认为如果在此 awk 代码中实现会更好。
    • @Newbie 是的,可以在打印周围设置条件,如果至少存在 1 个,则打印该行,然后不打印,这意味着在该行设置一个“打印”字段,设置当我们找到一个“名称”时它为真,编辑以反映这个变化
    【解决方案2】:

    stringrtidyr 可能不是最好的用法,但这可以在 hadleyverse 中以某种易读的方式完成...

    逻辑流程是:

    • 使用tidyr::fillifelse("Inspecting", rowname, NA) 确定组。
    • 将字段更改为您想要的内容
    • 使用 reshape (dcast) 获得您想要的格式。

    library(dplyr)
    library(tidyr)
    library(reshape2)
    library(stringr)
    
    is_in <- function(v1part) {
      return(ifelse(length(v1part) > 0, "B", "U"))
    }
    
    ab1<- ab %>% 
      add_rownames() %>%
      mutate(rowname = ifelse(V1=="Inspecting", rowname, NA),
             V4a = ifelse(V4 == "(-)" | V4 == "(+)", NA, V4),
    
             chr = str_extract_all(ab$V4, "^chr[^:]+", simplify = T)[,1],
             chr = ifelse(chr=="", NA, chr),
    
             start = str_split_fixed(V4a, ":|-", 3)[,2],
             start = ifelse(start=="", NA, start), 
    
             stop = str_split_fixed(V4a, ":|-", 3)[,3],
             stop = ifelse(stop=="", NA, stop),
    
             V1part = str_split_fixed(V1, "\\$|_", 3)[,2]) %>%
      fill(rowname, .direction="down") %>% 
      group_by(rowname) %>%
      fill(chr, .direction="down") %>%
      fill(start, .direction="down") %>%
      fill(stop, .direction="down") %>%
      dcast(chr+start+stop ~ V1part, fun.aggregate=is_in)
    
    > ab1
       chr     start      stop Var.4 ATF3 CEBPB YY1
    1 chr1 173244300 173244500     B    B     B   B
    2 chr1 173244350 173244550     B    B     B   U
    

    【讨论】:

    • 此列 Var.4 是什么?我可以从最终的 df 中删除它吗?
    • 是的,你可以,不知道发生了什么
    • 还有一些条目在 1 种情况下只存在 Inspecting sequence ID chr1:0-200,下面没有任何数据,我可以通过任何方式过滤掉这些条目吗?
    • 这样的事情也可以工作:cd &lt;- ab1[!which(ab1$ARID3A:ab1$YY1 == "U"),] 我告诉它不包括那些列的范围内都包含相同值的行 U
    • 或者类似的东西,如果在第 3 列之后每行都包含字母“U”,然后删除该行。所以在这种情况下,具有值的行:chr1 100 300 U U U U 将从 ab1 中删除。
    【解决方案3】:

    不优雅,但应该可以工作(您的数据有一列带有“|”...我将其命名为 df):

    cond <- which(!df$V2 == "|")
    new_df <- data.frame(chr=character(length(cond)), start=character(length(cond)), stop=character(length(cond)))
    
    for (i in 1:length(cond)) {
      line <- df[cond[i], ]
      var <- unlist(strsplit(line$V4, split = ":"))
      var2 <- unlist(strsplit(var[2], split = "-"))
      new_df$chr[i] <- var[1]
      new_df$start[i] <- var2[1]
      new_df$stop[i] <- var2[2]
      for (k in (i+1):(cond[i+1]-1)) {
        # Your code using name <- df$V1 (Use strsplit again)
        # df[i, name] <- ...
      }
    }
    

    【讨论】:

    • 我不明白# Your code using name &lt;- df$V1 (Use strsplit again) # df[i, name] &lt;- ... 是什么意思,请您给出一个可重复的答案。谢谢。
    • 系统和以前一样,varvar2。现在只需将$_ 作为split arg。
    • @cristoph 很抱歉,我无法理解您在说什么,请您在答案中编辑此更改。这将是一个很大的帮助。谢谢。
    • 好吧,我向你展示了如何使用strsplit。您如何尝试将V1$Row2 拆分为$_?什么不起作用?
    • 您能帮我解决这个question 用foreach 循环或parSapply 替换for 循环的问题吗?谢谢。
    猜你喜欢
    • 2016-09-15
    • 2019-08-07
    • 2017-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-29
    • 1970-01-01
    • 2015-05-16
    相关资源
    最近更新 更多