我有一个400+
标记名称的数据框,其中包含每个标记的每日时间序列数据。某些标签的值为0,我没有读数。我想计算时间序列中每个标签的非零金额的平均值,并用该平均值填充零值。
示例
tag1 day1 400
tag1 day2 200
tag1 day3 0
.
.
tag1 dayn 0
tag2 day1 0
tag2 day2 100
tag2 day3 0
...
这里我想用100填充tag2的0值,用300填充tag1的0值
我可以使用ddply对数据框进行子集化并计算均值,但我正在寻找基于每个标记的非零条目获取均值的最佳方法,然后用均值填充数据框中的原始零值每个标签的值。似乎有一些方法可以做一些代码行,但我怀疑有更多更快/更优雅的方法。数据中有400-500个标签,每日读数约为150个
答案 0 :(得分:2)
对于大型数据集,使用data.table
或dplyr
可能效率很高。
使用data.table
,我们将'data.frame'转换为'data.table'(setDT(df1)
),以避免因'Amount'的class
之间可能不匹配而发生冲突和mean
值(可能是'数字'类),我们可以先将'金额'更改为'数字'类(Amount := as.numeric(Amount)
),通过获取{{1}创建'均值'列根据'tag'分组的所有非零'Amount'值,将Amount(mean
)的值'0'替换为'Mean'列(Amount==0
)中的相应值,如果需要,我们可以通过指定'NULL'
Amount := Mean
或者使用 library(data.table)
setDT(df1)[, Amount:= as.numeric(Amount)
][, Mean:= mean(Amount[Amount!=0]), by= tag
][Amount==0, Amount := Mean][, Mean:= NULL]
,我们可以按'代码'进行分组,并使用dplyr
将'0'值更改为replace
mean
或可能的 library(dplyr)
df1 %>%
group_by(tag)%>%
mutate(Amount= replace(Amount, which(Amount==0),
mean(Amount[Amount!=0])))
解决方案
sqldf
如@Frank所述(在评论中),如果某个特定的“标记”组只有0作为“金额”,那么之前的解决方案将获得 library(sqldf)
res1 <- sqldf("select * from df1
left join (select tag,
avg(Amount) as Mean
from df1
where Amount is not 0
group by tag)
using (tag)")
sqldf("select tag, day,
case when Amount like 0
then Mean
else Amount
end Amount
from res1")
的“NaN”。在这种情况下,我不确定预期值是多少。假设我们需要将其保持为0,代码中的更改(包含mean
可能的NA值)
na.rm=TRUE
df2 <- rbind(df1,list("tag3","day3",0))
setDT(df2)[, Amount := as.numeric(Amount)
][, Mean:= if(all(Amount==0)) 0
else mean(Amount[Amount!=0], na.rm=TRUE), by = tag
][Amount==0, Amount:= Mean][, Mean:= NULL]
答案 1 :(得分:2)
以下是一些方法:
1)sqldf 这是从评论中删除的。以下代码选择tag
,day
和Amount
的两个值之一。对于每一行,如果Amount
为0,则它运行内部相关选择,否则使用Amount
值。
library(sqldf)
sqldf("select
tag,
day,
case when a.Amount = 0
then (select avg(b.Amount)
from df1 b
where b.Amount != 0 and b.tag = a.tag)
else a.Amount
end Amount
from df1 a")
,并提供:
tag day Amount
1 tag1 day1 400
2 tag1 day2 200
3 tag1 day3 300
4 tag1 dayn 300
5 tag2 day1 100
6 tag2 day2 100
7 tag2 day3 100
2)na.aggregate 用NA替换零值,然后使用na.aggregate
将ave
从动物园应用到每个组:
library(zoo)
transform(df1, Amount = ave(replace(Amount, Amount == 0, NA), tag, FUN = na.aggregate))
注意我们使用以下内容作为输入:
df1 <- structure(list(tag = c("tag1", "tag1", "tag1", "tag1", "tag2",
"tag2", "tag2"), day = c("day1", "day2", "day3", "dayn", "day1",
"day2", "day3"), Amount = c(400L, 200L, 0L, 0L, 0L, 100L, 0L)), .Names = c("tag",
"day", "Amount"), class = "data.frame", row.names = c(NA, -7L))
更新:已添加(2)。
答案 2 :(得分:0)
以下是使用ave()
的可能解决方案:
set.seed(2); NT <- 4; ND <- 4; df <- data.frame(tag=rep(paste0('tag',1:NT),each=ND),day=rep(paste0('day',1:ND),NT),amount=c(sample(seq(0,400,100),ND*(NT-1),replace=T),rep(0,ND)));
df;
## tag day amount
## 1 tag1 day1 0
## 2 tag1 day2 300
## 3 tag1 day3 200
## 4 tag1 day4 0
## 5 tag2 day1 400
## 6 tag2 day2 400
## 7 tag2 day3 0
## 8 tag2 day4 400
## 9 tag3 day1 200
## 10 tag3 day2 200
## 11 tag3 day3 200
## 12 tag3 day4 100
## 13 tag4 day1 0
## 14 tag4 day2 0
## 15 tag4 day3 0
## 16 tag4 day4 0
df$amount[df$amount==0] <- NA;
df$amount[is.na(df$amount)] <- ave(df$amount,df$tag,FUN=function(x) mean(x,na.rm=T))[is.na(df$amount)];
df;
## tag day amount
## 1 tag1 day1 250
## 2 tag1 day2 300
## 3 tag1 day3 200
## 4 tag1 day4 250
## 5 tag2 day1 400
## 6 tag2 day2 400
## 7 tag2 day3 400
## 8 tag2 day4 400
## 9 tag3 day1 200
## 10 tag3 day2 200
## 11 tag3 day3 200
## 12 tag3 day4 100
## 13 tag4 day1 NaN
## 14 tag4 day2 NaN
## 15 tag4 day3 NaN
## 16 tag4 day4 NaN
弗兰克关于NaNs的评论(关于akrun的帖子)也适用于此。如果您不想要NaN,我只需将其替换为零或NA或您想要的任何默认值,例如: df$amount[is.nan(df$amount)] <- NA;
。