I am working with taxonomic data and have gotten my data to the second last step before I can display it graphically. However, I need rows to match conditions and this is where I am stuck - well stuck because I do not want to do it manually.
My data:
我正在处理分类数据,并且在我以图形方式显示数据之前将数据提供给第二步。但是,我需要行来匹配条件,这就是我被卡住的地方 - 因为我不想手动操作而陷入困境。我的数据:
x <- data.frame("Phylum" = c("Chordata", "Chordata", "Chordata", "Chordata", "Chordata", "Chordata"),
"Class" = c("NA", "Actinopterygii", "Actinopterygii", "Actinopterygii", "Actinopterygii", "Actinopterygii"),
"Order" = c("NA", "NA", "Gadiformes", "Gadiformes", "Gadiformes", "Gadiformes"),
"Family" = c("NA", "NA", "NA", "Moridae", "Moridae", "Moridae"),
"Genus" = c("NA", "NA", "NA", "NA", "Notophycis", "Notophycis"),
"Species" = c("NA", "NA", "NA", "NA", "NA", "Notophycis marginata"),
Number = c(21616, 12123, 1497, 730,730,730))
The wanted end result:
想要的最终结果:
y <- data.frame("Phylum" = c("Chordata", "Chordata", "Chordata", "Chordata"),
"Class" = c("NA", "Actinopterygii", "Actinopterygii", "Actinopterygii"),
"Order" = c("NA", "NA", "Gadiformes", "Gadiformes"), "Family" = c("NA", "NA", "NA", "Moridae"),
"Genus" = c("NA", "NA", "NA", "Notophycis"), "Species" = c("NA", "NA", "NA", "Notophycis marginata"),
Number = c(9493, 10626, 767, 730))
This is a simple subset example from a much larger more complicated dataset. So if I could put this into code somehow:
这是来自更大更复杂的数据集的简单子集示例。所以如果我能以某种方式把它放到代码中:
- sum of Number (
Phylum == "P1" & Class == "NA"
) - sum of Number (Class == "C1" & Order == "NA"
) IF phylum matches and this would equal P1's new Number - sum of Number (
Class == "C1" & Order== "NA"
) - sum of Number (Order == "O1" & Family == "NA"
) IF class matches and this would equal C1's new Number etc...
数字之和(Phylum ==“P1”和类==“NA”) - 数字之和(类==“C1”和顺序==“NA”)如果门匹配,这将等于P1的新数字
数字之和(Class ==“C1”&Order ==“NA”) - 数字之和(Order ==“O1”和Family ==“NA”)IF类匹配,这将等于C1的新数字等。 。
BUT if the Number matches for multiple rows I need to have code to evaluate those rows and choose the row that has the least amount of NAs and keep that Number...
但是如果Number匹配多行,我需要有代码来评估这些行,并选择具有最少数量的NAs并保留该数量的行...
I would presume I am looking to code a function to do this, but have no idea where to even start!
我想我想要编写一个函数来做这个,但不知道从哪里开始!
Appreciate the help :)
感谢帮助:)
UPDATE
Tester:
Phylum Class Order Family Genus Species Reads_sum
Chordata Elasmobranchii Carcharhiniformes NA NA NA 31
Chordata Actinopterygii Perciformes Scombridae NA NA 589
Chordata Elasmobranchii Carcharhiniformes Pentanchidae NA NA 31
Chordata Actinopterygii Myctophiformes Myctophidae Notoscopelus NA 208
Chordata Actinopterygii Perciformes Scombridae Katsuwonus NA 589
Chordata Actinopterygii Myctophiformes Myctophidae Notoscopelus Notoscopelus caudispinosus 178
Chordata Actinopterygii Perciformes Scombridae Katsuwonus Katsuwonus pelamis 589
Cnidaria Hydrozoa Leptothecata Plumulariidae NA NA 69
Cnidaria Hydrozoa Leptothecata Plumulariidae Plumularia NA 69
Echinodermata Ophiuroidea NA NA NA NA 146
Echinodermata Ophiuroidea Ophiurida NA NA NA 137
Echinodermata Ophiuroidea Ophiurida Ophiuridae NA NA 137
Echinodermata Ophiuroidea Ophiurida Ophiuridae Ophioplinthus NA 137
Echinodermata Ophiuroidea Ophiurida Ophiuridae Ophioplinthus Ophioplinthus accomodata 137
Mollusca Cephalopoda Oegopsida Ommastrephidae NA NA 34311
Ochrophyta Phaeophyceae Ectocarpales Acinetosporaceae NA NA 29
Code that preforms what I would like BUT would have to change the variables each time:
预先形成我想要的代码但是每次都必须更改变量:
Tester$Reads_sum[Tester$Class == "Ophiuroidea" & Tester$Order == "NA"] - sum(Tester$Reads_sum[Tester$Class == "Ophiuroidea" & Tester$Order != "NA" & Tester$Family == "NA"])
And so I was hoping something like this would work and I would just need to change Class to other selected taxonomic ranks:
所以我希望这样的东西能起作用,我只需要将Class更改为其他选定的分类等级:
for (i in unique(Tester$Class)){
Tester$Test.1 <- ifelse(Tester$Class != "NA" & Tester$Order == "NA",
Tester$Reads_sum[Tester$Class == i & Tester$Order == "NA"] - sum(Tester$Reads_sum[Tester$Class == i & Tester$Order != "NA" & Tester$Family == "NA"]), 0)
}
But it is giving me an NA instead of 9.
但它给了我一个NA而不是9。
The end data should look like this:
最终数据应如下所示:
Phylum Class Order Family Genus Species Reads_sum
Chordata Elasmobranchii Carcharhiniformes Pentanchidae NA NA 31
Chordata Actinopterygii Myctophiformes Myctophidae Notoscopelus NA 30
Chordata Actinopterygii Myctophiformes Myctophidae Notoscopelus Notoscopelus caudispinosus 178
Chordata Actinopterygii Perciformes Scombridae Katsuwonus Katsuwonus pelamis 589
Cnidaria Hydrozoa Leptothecata Plumulariidae Plumularia NA 69
Echinodermata Ophiuroidea NA NA NA NA 9
Echinodermata Ophiuroidea Ophiurida Ophiuridae Ophioplinthus Ophioplinthus accomodata 137
Mollusca Cephalopoda Oegopsida Ommastrephidae NA NA 34311
Ochrophyta Phaeophyceae Ectocarpales Acinetosporaceae NA NA 29
1 个解决方案
#1
0
thanks for the update. I've come up with something I think meets what you are looking for, but will need some support.
感谢更新。我想出了一些我认为符合您要求的东西,但需要一些支持。
Am I right in thinking its a tree like data in the order c("Phylum", "Class", "Order", "Family", "Genus", "Species")
? and that you are interested in finding for each level of the tree, you want to remove the values of the layer below?
我是否正确地认为它的树类如c(“Phylum”,“Class”,“Order”,“Family”,“Genus”,“Species”)中的数据?并且您有兴趣找到树的每个级别,您想要删除下面的图层的值?
I hope my code isn't too confusing, I found the data challenging to use in its current format. I prefer to split it in to the levels of the tree, ie the ones that just have Phylum data, all the way down to the ones that have all levels of the tree. To do so, I am most comfortable using the data.table
package.
我希望我的代码不会太混乱,我发现数据在当前格式中难以使用。我更喜欢将它分成树的级别,即只有Phylum数据的级别,一直到具有树的所有级别的那些。为此,我最熟悉使用data.table包。
I've used lapply's
where I can as I find them easy to interpret once you use them a lot. I'm sure there will be a more efficient solution out there, but as a starter, I think knowing and understanding the steps required are more important.
我已经使用了lapply,因为我发现它们很容易解释,一旦你使用它们很多。我相信会有一个更有效的解决方案,但作为首发,我认为了解和理解所需的步骤更为重要。
# using data.table package, as I find it quicker and easier to work with
# for complex problems. Run the hashed out command below if you dont have it
# install.packages("data.table")
library(data.table)
# turning in to a data.table, similar to data.frame, but some differences.
dt <- as.data.table(Tester)
# I am making an id, which I will use to split up this data. Different rows
# have different structures, as its a tree structure, so I am going to break
# the data up
dt[, id := 1:.N]
# to do so i need to know the order of significance of the tree. I believe
# they go in this order:
col_structure <- c("Phylum", "Class", "Order", "Family", "Genus", "Species")
# I want to find out at which level of the tree each row is, so I am going
# to change teh shape from wide to long, and then do some row aggregation on
# the single column, to group
melt_dt <- melt(dt, id.vars = "id",
measure.vars = col_structure)
# tip: try not to use "NA", but instead NA, they have different structures
# and built in commands like is.na make them easier to differentiate
melt_dt[value == "NA", value := NA]
melt_dt <- melt_dt[!is.na(value)]
melt_dt[]
# using a data.table command .N, grouped by id, to find out how many non NA
# values there are, this will tell me where it is in the tree
group_ids <- melt_dt[, .N, by = id]
# Ok, so now I will split up each row in to where it sits in the tree
split_ids <- split(group_ids, group_ids$N)
split_ids
# pull out the number of levels of tree for easy use
levels <- seq_along(split_ids)
# merge back in the original data, so we have the same data at the start, but
# split up in to new sets. Makes it easier to think about the problem
split_dt <- lapply(levels, function(x){
out <- merge(split_ids[[x]], dt, by = "id")
N <- as.numeric(names(split_ids)[x])
# using keys in my data, to make easy extraction. means rather than do
# Phylum == "a" & Class == "b" later on, if Phylum & Class are the keys,
# then can use command J("a", "b"). See next stage
setkeyv(out, col_structure[1:N])
out
})
# Now I'm going to add the value in. I will look at the next level of the tree
# and remove the values from that level from the reads_sum. Try it with setting
# x = 1.
# I've removed bottom element of the tree, don't know what to do with them
split_dt_with_value <- lapply(levels[1:(length(levels)-1)], function(x){
# similar to for loop, but using data.table keys to extract data
out <- split_dt[[x]]
out$Test.1 <- out$Reads_sum - sapply(1:nrow(out), function(i){
sum(split_dt[[(x+1)]][J(out[i, key(out), with = FALSE])]$Reads_sum,
na.rm = TRUE)
})
out
})
# combine results, and with the bottom tree level
combined <- rbindlist(c(split_dt_with_value,
split_dt[max(levels)]),
fill = TRUE)
# turn it back in to data frame form
combined <- as.data.frame(combined)
combined
please have a look and let me know if any steps are confusing, or any of the logic is incorrect :)
请看看,如果任何步骤令人困惑,或任何逻辑不正确,请告诉我:)
Cheers, Jonny
#1
0
thanks for the update. I've come up with something I think meets what you are looking for, but will need some support.
感谢更新。我想出了一些我认为符合您要求的东西,但需要一些支持。
Am I right in thinking its a tree like data in the order c("Phylum", "Class", "Order", "Family", "Genus", "Species")
? and that you are interested in finding for each level of the tree, you want to remove the values of the layer below?
我是否正确地认为它的树类如c(“Phylum”,“Class”,“Order”,“Family”,“Genus”,“Species”)中的数据?并且您有兴趣找到树的每个级别,您想要删除下面的图层的值?
I hope my code isn't too confusing, I found the data challenging to use in its current format. I prefer to split it in to the levels of the tree, ie the ones that just have Phylum data, all the way down to the ones that have all levels of the tree. To do so, I am most comfortable using the data.table
package.
我希望我的代码不会太混乱,我发现数据在当前格式中难以使用。我更喜欢将它分成树的级别,即只有Phylum数据的级别,一直到具有树的所有级别的那些。为此,我最熟悉使用data.table包。
I've used lapply's
where I can as I find them easy to interpret once you use them a lot. I'm sure there will be a more efficient solution out there, but as a starter, I think knowing and understanding the steps required are more important.
我已经使用了lapply,因为我发现它们很容易解释,一旦你使用它们很多。我相信会有一个更有效的解决方案,但作为首发,我认为了解和理解所需的步骤更为重要。
# using data.table package, as I find it quicker and easier to work with
# for complex problems. Run the hashed out command below if you dont have it
# install.packages("data.table")
library(data.table)
# turning in to a data.table, similar to data.frame, but some differences.
dt <- as.data.table(Tester)
# I am making an id, which I will use to split up this data. Different rows
# have different structures, as its a tree structure, so I am going to break
# the data up
dt[, id := 1:.N]
# to do so i need to know the order of significance of the tree. I believe
# they go in this order:
col_structure <- c("Phylum", "Class", "Order", "Family", "Genus", "Species")
# I want to find out at which level of the tree each row is, so I am going
# to change teh shape from wide to long, and then do some row aggregation on
# the single column, to group
melt_dt <- melt(dt, id.vars = "id",
measure.vars = col_structure)
# tip: try not to use "NA", but instead NA, they have different structures
# and built in commands like is.na make them easier to differentiate
melt_dt[value == "NA", value := NA]
melt_dt <- melt_dt[!is.na(value)]
melt_dt[]
# using a data.table command .N, grouped by id, to find out how many non NA
# values there are, this will tell me where it is in the tree
group_ids <- melt_dt[, .N, by = id]
# Ok, so now I will split up each row in to where it sits in the tree
split_ids <- split(group_ids, group_ids$N)
split_ids
# pull out the number of levels of tree for easy use
levels <- seq_along(split_ids)
# merge back in the original data, so we have the same data at the start, but
# split up in to new sets. Makes it easier to think about the problem
split_dt <- lapply(levels, function(x){
out <- merge(split_ids[[x]], dt, by = "id")
N <- as.numeric(names(split_ids)[x])
# using keys in my data, to make easy extraction. means rather than do
# Phylum == "a" & Class == "b" later on, if Phylum & Class are the keys,
# then can use command J("a", "b"). See next stage
setkeyv(out, col_structure[1:N])
out
})
# Now I'm going to add the value in. I will look at the next level of the tree
# and remove the values from that level from the reads_sum. Try it with setting
# x = 1.
# I've removed bottom element of the tree, don't know what to do with them
split_dt_with_value <- lapply(levels[1:(length(levels)-1)], function(x){
# similar to for loop, but using data.table keys to extract data
out <- split_dt[[x]]
out$Test.1 <- out$Reads_sum - sapply(1:nrow(out), function(i){
sum(split_dt[[(x+1)]][J(out[i, key(out), with = FALSE])]$Reads_sum,
na.rm = TRUE)
})
out
})
# combine results, and with the bottom tree level
combined <- rbindlist(c(split_dt_with_value,
split_dt[max(levels)]),
fill = TRUE)
# turn it back in to data frame form
combined <- as.data.frame(combined)
combined
please have a look and let me know if any steps are confusing, or any of the logic is incorrect :)
请看看,如果任何步骤令人困惑,或任何逻辑不正确,请告诉我:)
Cheers, Jonny