【问题标题】:How to lookup by row and column > column names?如何按行和列>列名查找?
【发布时间】:2017-04-29 10:43:11
【问题描述】:

我正在考虑如何按大学名称(第一行:A,...,F)、字段名称(第一列:Acute,.. .,En) 和/或毕业时间 (time) 在以下文件 DS.csv 中。 我在想dplyr 方法,但无法将数字 ID 查找(线程答案How to overload function parameters in R?)扩展到三个变量的查找。 挑战

  1. 如何按第一行查找?也许,类似于$1 == "A"
  2. 如何将大学查询扩展到两列?伪代码$1 == "A" 大约是第二列和第三列,...,$1 == "F" 大约是最后两列。
  3. 通过 3 个查找条件进行查找:第一行(无标题)、带有标题 Field 的第一列和标题 time。伪代码

    times <- getTimes($1 == "A", Field == "Ane", by = "desc(time)")
    

数据DS.csv 有数据。第一列表示实验。以下数据采用交叉表格式,因此

,A,,B,,C,,D,,E,,F,
Field,time,T,time,T,time,T,time,T,time,T,time,T
Acute,0,0,8.3,1,7.5,1,8.6,2,0,0,8.3,4
Ane,9,120,7.7,26,7.9,43,7.8,77,7.9,60,8.2,326
En,15.6,2,12.9,1,0,0,0,0,14.3,1,14.6,4
Fo,9.2,2,0,0,5.4,1,0,0,0,0,7.9,3

并采用直表格式,这样

Field,time,T,Experiment       
Acut,0,0,A
An,9,120,A
En,15.6,2,A
Fo,9.2,2,A
Acute,8.3,1,B       
An,7.7,26,B
En,12.9,1,B
Fo,0,0,B  
Acute,7.5,1,C       
An,7.9,43,C
En,0,0,C  
Fo,5.4,1,C
Acute,8.6,2,D       
An,7.8,77,D
En,0,0,D  
Fo,0,0,D  
Acute,0,0,E         
An,7.9,60,E
En,14.3,1,E
Fo,0,0,E  
Acute,8.3,4,F       
An,8.2,326,F
En,14.6,4,F
Fo,7.9,3,F

伪代码

library('dplyr')
ow <- options("warn")
DF <- read.csv("/home/masi/CSV/DS.csv", header = T)

# Lookup by first row, Lookup by Field, lookup by Field's first column?
times <- getTimes($1 == "A", Field == "Ane", by = "desc(time)")

预期输出:9
广义预期输出:a, b, c, ...

## Data where values marked by small letters a,b,c, ... are wanted 
#       uni1    uni2 ... 
#       time T  time T ...
#Field1 a       c
#Field2 b       ...
#...    ...

R:3.3.3 (2017-03-06)
操作系统:Debian 8.7
硬件:华硕 Zenbook UX303UA

【问题讨论】:

  • @hhh 我已经回滚了你的编辑。您应该使用 OP 发布的数据,而不是插入您自己的数据表示。
  • 但是您知道如何将原始数据转换为编辑后的数据吗?如果没有,你不应该将它包含在你的问题中。然后由回答的人获得所需的格式。

标签: r dplyr


【解决方案1】:

以您的初始原始数据为起点:

# read the data & skip 1st & 2nd line which contain only header information
DF <- read.csv(text=",A,,B,,C,,D,,E,,F,
Field,time,T,time,T,time,T,time,T,time,T,time,T
Acute,0,0,8.3,1,7.5,1,8.6,2,0,0,8.3,4
Ane,9,120,7.7,26,7.9,43,7.8,77,7.9,60,8.2,326
En,15.6,2,12.9,1,0,0,0,0,14.3,1,14.6,4
Fo,9.2,2,0,0,5.4,1,0,0,0,0,7.9,3", header=FALSE, stringsAsFactors=FALSE, skip=2)

# read the first two lines which contain the header information
headers <- read.csv(text=",A,,B,,C,,D,,E,,F,
Field,time,T,time,T,time,T,time,T,time,T,time,T
Acute,0,0,8.3,1,7.5,1,8.6,2,0,0,8.3,4
Ane,9,120,7.7,26,7.9,43,7.8,77,7.9,60,8.2,326
En,15.6,2,12.9,1,0,0,0,0,14.3,1,14.6,4
Fo,9.2,2,0,0,5.4,1,0,0,0,0,7.9,3", header=FALSE, stringsAsFactors=FALSE, nrow=2)

# extract the university names for the 'headers' data.frame
universities <- unlist(headers[1,])
universities <- universities[universities != '']

# create column names from the 'headers' data.frame
vec <- headers[2,][headers[2,] == 'T']
headers[2,][headers[2,] == 'T'] <- paste0(vec, seq_along(vec))
names(DF) <- paste0(headers[2,],headers[1,])

您的数据框现在如下所示:

> DF
   Field timeA  T1 timeB T2 timeC T3 timeD T4 timeE T5 timeF  T6
1: Acute   0.0   0   8.3  1   7.5  1   8.6  2   0.0  0   8.3   4
2:   Ane   9.0 120   7.7 26   7.9 43   7.8 77   7.9 60   8.2 326
3:    En  15.6   2  12.9  1   0.0  0   0.0  0  14.3  1  14.6   4
4:    Fo   9.2   2   0.0  0   5.4  1   0.0  0   0.0  0   7.9   3

因为最好将数据转换成长格式:

library(data.table)
DT <- melt(setDT(DF), id = 1, 
           measure.vars = patterns('^time','^T'),
           variable.name = 'university', 
           value.name = c('time','t')
           )[, university := universities[university]][]

现在您的数据如下所示:

> DT
    Field university time   t
 1: Acute          A  0.0   0
 2:   Ane          A  9.0 120
 3:    En          A 15.6   2
 4:    Fo          A  9.2   2
 5: Acute          B  8.3   1
 6:   Ane          B  7.7  26
 7:    En          B 12.9   1
 8:    Fo          B  0.0   0
 9: Acute          C  7.5   1
10:   Ane          C  7.9  43
11:    En          C  0.0   0
12:    Fo          C  5.4   1
13: Acute          D  8.6   2
14:   Ane          D  7.8  77
15:    En          D  0.0   0
16:    Fo          D  0.0   0
17: Acute          E  0.0   0
18:   Ane          E  7.9  60
19:    En          E 14.3   1
20:    Fo          E  0.0   0
21: Acute          F  8.3   4
22:   Ane          F  8.2 326
23:    En          F 14.6   4
24:    Fo          F  7.9   3

现在您可以选择所需的信息:

 DT[university == 'A' & Field == 'Ane']

给出:

   Field university time   t
1:   Ane          A    9 120

几个dplyr 过滤数据的例子:

library(dplyr)
DT %>% 
  filter(Field=="En" & t > 1)

给予:

  Field university time t
1    En          A 15.6 2
2    En          F 14.6 4

或者:

DT %>%
  arrange(desc(time)) %>%
  filter(time < 14 & t > 3)

给予:

  Field university time   t
1   Ane          A  9.0 120
2 Acute          F  8.3   4
3   Ane          F  8.2 326
4   Ane          C  7.9  43
5   Ane          E  7.9  60
6   Ane          D  7.8  77
7   Ane          B  7.7  26

【讨论】:

  • @LéoLéopoldHertz준영 我添加了一个关于如何使用dplyr 进行过滤的示例。我使用data.table-package 来重塑为长格式,因为它比tidyr 包更灵活,特别是因为可以同时创建多个值列。关于您评论中的代码:您应该使用&amp; 而不是逗号。
  • @LéoLéopoldHertz준영 是的,我不明白:它是无效的 R 语法。更好:DF[order(-time)][time &lt; 14 &amp; t &gt; 3](如果是 data.table)或 DF[order(-DF$time)][DF$time &lt; 14 &amp; DF$t &gt; 3, ] 如果是 data.frame(请注意末尾的额外逗号)。
  • @LéoLéopoldHertz준영 这取决于你的习惯,但使用 dplyr 是:DF %&gt;% arrange(desc(time)) %&gt;% filter(time &lt; 14 &amp; t &gt; 3)
  • @LéoLéopoldHertz준영 那就是过滤的data.table。另见我的previous comment。 data.frame 方法为:DF[DF$Experiment=="A", ]
  • @LéoLéopoldHertz준영 更多信息:(1) an introduction into data.table 和 (2) an intro on dplyr
【解决方案2】:

改变你的交叉表

,A,,B,,C,,D,,E,,F,
Field,time,T,time,T,time,T,time,T,time,T,time,T
Acute,0,0,8.3,1,7.5,1,8.6,2,0,0,8.3,4
Ane,9,120,7.7,26,7.9,43,7.8,77,7.9,60,8.2,326
En,15.6,2,12.9,1,0,0,0,0,14.3,1,14.6,4
Fo,9.2,2,0,0,5.4,1,0,0,0,0,7.9,3

转换成直接的数据格式

Field,time,T,Experiment
Acut,0,0,A
An,9,120,A
En,15.6,2,A
Fo,9.2,2,A
Acute,8.3,1,B
An,7.7,26,B
En,12.9,1,B
Fo,0,0,B
Acute,7.5,1,C
An,7.9,43,C
En,0,0,C
Fo,5.4,1,C
Acute,8.6,2,D
An,7.8,77,D
En,0,0,D
Fo,0,0,D
Acute,0,0,E
An,7.9,60,E
En,14.3,1,E
Fo,0,0,E
Acute,8.3,4,F
An,8.2,326,F
En,14.6,4,F
Fo,7.9,3,F

我在哪里使用 Vim.csv 插件和可视块模式。

多种选择方式

在将数据整理成易于格式化的直表(不是交叉表)后,这很容易以多种方式完成,我更喜欢 SQL。我在下面演示了 SQLDDF 包,它在处理大数据时效率非常低,但它很小,所以它可以工作。

除了read.csv 等非常低效的内置函数,我会参考data.table 包中非常有效的fread 来读取文件。

SQLDF

> library(data.table);
> a<-fread("~/DS_straight_table.csv");
> sqldf("select time from a where Experiment='A' and Field='An'")
  time
1    9

其他不带sqldf的

> library(data.table);
> a<-fread("~/DS_straight_table.csv");
> a[Experiment=='A' & Field=='An'] 
Field time   T Experiment
1:    An    9 120          A

【讨论】:

    【解决方案3】:

    使用“Tall”(直桌)格式和库 dplyr。您的数据在每个字段(实验)中只有一个值。

    library(dplyr)    
    
    ## this is the more general result
    df %>% 
      group_by(Field, Experiment) %>%
      top_n(1, wt = -time)
    
    
    ## example function
    getTimes<- function(data, field, experiment) {
      data %>% 
        filter(Field == field, Experiment == experiment) %>%
        top_n(1, wt = -time)
    }
    
    
    getTimes(df, 'An', 'A')
    
    #   Field time   T Experiment
    # 1    An    9 120          A
    

    【讨论】:

    • .. 由@hhh 给出
    • 我正在考虑按time 应用降序排列,例如DF[time &lt; 14 &amp; t &gt; 3 &amp; by = "desc(time)"]。我不能让它与data.table 包一起工作,所以我认为dplyr 应该可以工作。你怎么看?
    • 在上面的top_n -time 的顺序选择第一行——减号“-”使它降序,所以这选择了最新的时间。如果您只想订购它,那么您可以使用arrange(time) arrange (-time)。更正确的是使用arrange(desc(time)),因为 desc 适用于字符和数字。
    • @epi99 是的。您可以将排序集成到函数getTimes 的参数中吗?这应该是可能的。等等getTimes(df, Field='An', T='A', desc(time))。 - - 你也可以像我的例子一样严格传递参数。我认为这比说第二个参数是这个,第三个参数是这个要好。函数参数也可以是time &lt; 5等条件。
    猜你喜欢
    • 1970-01-01
    • 2022-07-23
    • 1970-01-01
    • 2012-11-08
    • 1970-01-01
    • 1970-01-01
    • 2021-02-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多