[英]asssign values to dataframe subset in R
我在將數據幀分配給另一個子集時遇到麻煩。 在下面的示例中,
ds[cavities,] <- join(ds[cavities,1:4], fillings, by="ZipCode", "left")
僅修改一列而不是兩列。 我希望它要么不修改任何列,要么不修改兩個列,而不僅僅是一個。 我通過將ds
連接到另一個數據框cs
編寫了函數,以填充數據CountyID
ds
中PrefName
和CountyID
列(它們是NA
。
如您所見,如果沒有運行它,則測試將失敗,因為PrefName
未被填充。在進行了一些調試之后,我意識到join()
確實在執行預期的操作,但實際上是對該連接的結果將PrefName
NA
。
# fully copy-paste-run-able (but broken) code
suppressMessages({
library("plyr")
library("methods")
library("testthat")
})
# Fill in the missing PrefName/CountyIDs in delstat
# - Find the missing values in Delstat
# - Grab the CityState Primary Record values
# - Match on zipcode to fill in the holes in the delstat data
# - Remove any codes that could not be fixed
# - @param ds: delstat dataframe with 6 columns (see test case)
# - @param cs: citystate dataframe with 6 columns (see test case)
getMissingCounties <- function(ds, cs) {
if (length(is.na(ds$CountyID))) {
cavities <- which(is.na(ds$CountyID))
fillings <- cs[cs$PrimRec==TRUE, c(1,3,4)]
ds[cavities,] <- join(ds[cavities,1:4], fillings, by="ZipCode", "left")
ds <- ds[!is.na(ds$CountyID),]
}
return(ds)
}
test_getMissingCounties <- function() {
ds <- data.frame(
CityStateKey = c(1, 2, 3, 4 ),
ZipCode = c(11, 22, 33, 44 ),
Business = c(1, 1, 1, 1 ),
Residential = c(1, 1, 1, 1 ),
PrefName = c("One", NA , NA, NA),
CountyID = c(111, NA, NA, NA))
cs <- data.frame(
ZipCode = c(11, 22, 22, 33, 55 ),
Name = c("eh", "eh?", "eh?", "eh!?", "ah." ),
PrefName = c("One", "To", "Two", "Three", "Five"),
CountyID = c(111, 222, 222, 333, 555 ),
PrimRec = c(TRUE, FALSE, TRUE, TRUE, TRUE ),
CityStateKey = c(1, 2, 2, 3, 5 ))
expected <- data.frame(
CityStateKey = c(1, 2, 3 ),
ZipCode = c(11, 22, 33 ),
Business = c(1, 1, 1 ),
Residential = c(1, 1, 1 ),
PrefName = c("One", "Two", "Three"),
CountyID = c(111, 222, 333 ))
expect_equal(getMissingCounties(ds, cs), expected)
}
# run the test
test_getMissingCounties()
結果是:
CityStateKey ZipCode Business Residential PrefName CountyID
1 11 1 1 One 111
2 22 1 1 <NA> 222
3 33 1 1 <NA> 333
有什么想法為什么PrefName
會被分配設置為NA
或如何進行分配,以免丟失數據?
簡短的答案是,可以通過確保數據幀中沒有任何因素來避免此問題。 您可以通過在data.frame(...)
的調用中使用stringsAsFactors=FALSE
來data.frame(...)
。 請注意,默認情況下,許多數據導入功能(包括read.table(...)
和read.csv(...)
也會將字符轉換為因數。 您可以用相同的方法來擊敗這種行為。
這個問題實際上是非常微妙的,並且也是R在數據類型之間的“沉默強制”如何造成各種問題的一個很好的例子。
data.frame(...)
函數默認將任何字符向量轉換為因子。 因此,在您的代碼中, ds$PerfName
是一個具有一個級別的因子,而cs$PerfName
是一個具有五個級別的因子。 因此,在您的工作分配聲明中:
ds[cavities,] <- join(ds[cavities,1:4], fillings, by="ZipCode", "left")
LHS的第5列是1級因子,RHS的第5列是5級因子。
在某些情況下 ,當您將具有較高級別的因子分配給具有較少級別的因子時,缺少的級別將設置為NA
。 考慮一下:
x <- c("A","B",NA,NA,NA) # character vector
y <- LETTERS[1:5] # character vector
class(x); class(y)
# [1] "character"
# [1] "character"
df <- data.frame(x,y) # x and y coerced to factor
sapply(df,class) # df$x and df$y are factors
# x y
# "factor" "factor"
# assign rows 3:5 of col 2 to col 1
df[3:5,1] <- df[3:5,2] # fails with a warning
# Warning message:
# In `[<-.factor`(`*tmp*`, iseq, value = 3:5) :
# invalid factor level, NA generated
df # missing levels set to NA
# x y
# 1 A A
# 2 B B
# 3 <NA> C
# 4 <NA> D
# 5 <NA> E
上面的示例等效於您的賦值語句。 但是,請注意如果將第2列的全部分配給第1列會發生什么。
# assign all of col 2 to col 1
df <- data.frame(x,y)
df[,1] <- df[,2] # succeeds!!
df
# x y
# 1 A A
# 2 B B
# 3 C C
# 4 D D
# 5 E E
這可行。
最后,關於調試的說明:如果要調試函數,有時在命令行 (例如,在全局環境中)逐行運行語句會很有用。 如果這樣做,您將得到上面的警告,而在函數調用中,警告被抑制。
可以通過以下方式重新實現getMissingCountries
來滿足測試的約束:
merge(ds[1:4], subset(subset(cs, PrimRec)[c(1, 3, 4)]), by="ZipCode")
注意:總是首先發出ZipCode列,這與您的預期結果不同。
但是要回答子分配問題:它會中斷,因為PrefName
的級別集在ds
和cs
之間不兼容。 避免使用因素或重新relevel
它們。 您可能已經錯過了R對此的警告,因為test那以某種方式抑制了警告。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.