如何使用不同类别的列向量转换数据框(一列为character
,下一列为numeric
,另一列为logical
等),方式为: 保留类/数据类型信息?
示例数据:
mydata <- data.frame(
col0 = c("row1", "row2", "row3"),
col1 = c(1, 2, 3),
col2 = letters[1:3],
col3 = c(TRUE, FALSE, TRUE)
)
这里还有一个xlsx
文件,其中包含两个数据方向的示例:https://github.com/rappster/stackoverflow/blob/master/excel/row-and-column-based-data.xlsx
一个简单的t()
或更多涉及的例程(如this post中建议的例程)很棒,但不保留类信息或原始数据框的列。我也知道类data.frame
从未打算在其列中存储混合类信息。
但是,至少我希望能够尽可能简单地“颠倒”data.frame
的方式:将视角置于行而不是专栏透视。即,行向量中的所有元素都需要属于同一个类,而类可以在列向量之间有所不同。
我经常在人们习惯用水平(“变量在行中”)代替时间序列数据的项目中工作,而不是垂直(我们在R中习惯的“变量是列中的”方向(而且,恕我直言,也使更多更有意义)。
更重要的是,他们广泛使用MS Excel。我需要通过XLConnect
和/或openxlsx
将公式直接从R写入文件,以“宽格式”和“更新现有Excel文件”读取数据(而不是能够在R中进行我的计算,然后只是转储最终结果在Excel文件中)。
虽然我经常试图告诉他们使用这样的定位意味着跨越语言/工具的既定标准(至少对于R和MS Excel来说是这样),但他们不太可能会切换。所以我必须以某种方式解决这个问题。
所以我虽然保留了基础list
,但尽可能让它“外观和感觉”像data.frame
。它有效,但非常复杂。我认为可能会有更聪明的解决方案。
功能定义:
transpose <- function (
x,
col = character(),
rnames_or_col = c("col", "rnames")
) {
rnames_or_col <- match.arg(rnames_or_col, c("col", "rnames"))
## Buffering column names //
cnames <- if (length(col)) {
x[[col]]
} else {
make.names(1:nrow(x))
}
## Removing anchoring column //
if (inherits(x, "data.table")) {
x <- as.data.frame(x, stringsAsFactors = FALSE)
}
## I don't like this part. Any suggestions on how a) build on top of existing
## data.table functionality b) the easiest way to make a data.table behave
## like a data.frame when indexing (remove operation below will yield
## "undesired" results from a data.frame perspective; it's fine in from
## data.table's perspective/paradigm of course)
if (length(col)) {
x <- x[ , -which(names(x) == col)]
}
## Buffer classes //
classes <- lapply(x, class)
## Buffer row names //
rnames <- names(x)
## Listify //
x <- lapply(as.list(x), function(row) {
df <- do.call(data.frame, list(as.list(row), stringsAsFactors = FALSE))
names(df) <- cnames
df
})
names(x) <- rnames
## Actual row names or row names as first column //
if (rnames_or_col == "col") {
x <- lapply(x, function(ii) {
data.frame(variable = row.names(ii), ii,
stringsAsFactors = FALSE, row.names = NULL, check.names = FALSE)
})
}
## Class //
class(x) <- c("df_transposed", class(x))
x
}
打印方式:
print.df_transposed <- function(object) {
cat("df_transposed: \n")
out <- do.call(rbind, object)
rownames(out) <- NULL
print(out)
}
Getter和setter方法:
"[<-.df_transposed" <- function(x, i, j, value) {
x[[i]][ , j] <- value
x
}
"[.df_transposed" <- function(x, i, j, drop = FALSE) {
# foo <- function(x, i, j, drop = FALSE) {
has_i <- !missing(i)
has_j <- !missing(j)
cls <- class(x)
scope <- if (has_i) {
i
} else {
1:length(x)
}
out <- lapply(unclass(x)[scope], function(ii) {
nms <- names(ii)
if (has_j) {
tmp <- ii[ , j, drop = drop]
names(tmp) <- nms[j]
## --> necessary due to `check.names` missing for `[.data.frame` :-/
tmp
} else {
ii
}
})
class(out) <- cls
out
}
班级职能:
class2 <- function(x) {
sapply(x, function(ii) {
value <- if ("variable" %in% names(ii)) {
unlist(ii[, -1])
} else {
unlist(ii)
}
class(value)
})
}
示例数据:
mydata <- data.frame(
col0 = c("row1", "row2", "row3"),
col1 = c(1, 2, 3),
col2 = letters[1:3],
col3 = c(TRUE, FALSE, TRUE)
)
实际转置和打印方法:
> (df_t <- transpose(mydata, col = "col0"))
df_transposed:
variable row1 row2 row3
1 col1 1 2 3
2 col2 a b c
3 col3 TRUE FALSE TRUE
> (df_t2 <- transpose(mydata, col = "col0", rnames_or_col = "rnames"))
df_transposed:
row1 row2 row3
col1 1 2 3
col2 a b c
col3 TRUE FALSE TRUE
打印未打开的对象:
> unclass(df_t)
$col1
variable row1 row2 row3
1 col1 1 2 3
$col2
variable row1 row2 row3
1 col2 a b c
$col3
variable row1 row2 row3
1 col3 TRUE FALSE TRUE
> unclass(df_t2)
$col1
row1 row2 row3
col1 1 2 3
$col2
row1 row2 row3
col2 a b c
$col3
row1 row2 row3
col3 TRUE FALSE TRUE
班级查询:
> class2(df_t)
col1 col2 col3
"numeric" "character" "logical"
索引:
> dat_t[1, ]
df_transposed:
variable row1 row2 row3
1 1 1 2 3
> dat_t[, 1]
df_transposed:
variable
1 1
2 1
3 1
>
> dat_t[1, 2]
df_transposed:
row1
col1 1
> dat_t[2, 3]
df_transposed:
row2
col2 b
>
> dat_t[1:2, ]
df_transposed:
variable row1 row2 row3
1 1 1 2 3
2 1 a b c
> dat_t[, 1:3]
df_transposed:
variable row1 row2
1 1 1 2
2 1 a b
3 1 TRUE FALSE
>
> dat_t[c(1, 3), 2:4]
df_transposed:
row1 row2 row3
col1 1 2 3
col3 1 0 1