繁体   English   中英

将唯一值拆分为多个列的单独列

[英]Split unique values into separate columns for multiple columns

我的数据的每个列都将重新缩放并放入0到100的bin中。bin列将用作模型的特征。 为了分别测试每个bin,我想将每个bin列分为每个值的单独列。 新列将保持为0或1,具体取决于单元格中的值是否与列的bin匹配。 从这样的事情:

row values
  1     10
  2     20
  3     30
  4     40
  5     10
  6     30
  7     40

对此:

row values_10 values_20 values_30 values_40
  1         1         0         0         0
  2         0         1         0         0
  3         0         0         1         0
  4         0         0         0         1
  5         1         0         0         0
  6         0         0         1         0
  7         0         0         0         1

这种蛮力方法可以完成工作,但是必须有更好的(非循环)方法:

values <- c( 10,20,30,40,10,30,40)
dat <- data.frame(values)

columnNames <- unique(dat$values)

for( n in 1:length(columnNames) )
{
    dat[as.character(columnNames[n])]  <- 0
}

columnNames2 <- colnames(dat)

for( c in 2:ncol(dat))
{
    hdr <- columnNames2[c]

    for( r in 1:nrow(dat))
    {
        if( dat$values[r]==as.integer(hdr) )
            dat[r,c]=1
    }
}

非常感谢!!

编辑

这些都是很好的答案,谢谢大家。 最终对象(无论是矩阵,表还是data.table)将仅包含单独的bin列(不包含源列)。 以下解决方案如何用于2000多个源列?

编辑2

根据对我的后续问题的答案,以下是针对将来遇到此问题的任何人的每种方法的实现。

# read in some data with multiple columns

df_in  <- read.table(text="row val1 val2
                  1     10     100
                  2     20     200
                  3     30     300
                  4     40     400
                  5     10     100
                  6     30     300
                  7     40     400", header=TRUE, stringsAsFactors=FALSE)

#   @Zelazny7 's method using a matrix

df_in$row <- NULL

col_names <- names(df_in)

for( c in 1:length(col_names)){

    uniq <- unlist(unique(df_in[col_names[c]]))

    m <- matrix(0, nrow(df_in), length(uniq), 
                dimnames = list(NULL, paste0(col_names[c], "_", uniq)))

    for (i in seq_along(df_in[[col_names[c]]])) {
        k <- match(df_in[[col_names[c]]][i], uniq, 0)
        m[i,k] <- 1
    }

    if( c==1 )
        df_out <- m
    else
        df_out <- cbind(df_out,m)
}


#   @P Lapointe 's method using 'table'

col_names <- names(df_in)

for( c in 2:length(col_names)){

    m <- table(df_in$row,df_in[[col_names[c]]])    
    uniq <- unlist(unique(df_in[col_names[c]]))
    newNames <- toString(paste0(col_names[c],'_',uniq))

    if( c==2 ){
        df_out <- m
        hdrs <- newNames
    }
    else{
        df_out <- cbind(df_out,m)
        hdrs <- paste(hdrs,newNames,sep=", ")
    }
}

colnames(df_out) <- unlist(strsplit(hdrs, split=", "))


#   @bdemarest 's method using 'data.table'
#   read in data first

library(data.table)

df_in = fread("row val1 val2
            1     10     100
            2     20     200
            3     30     300
            4     40     400
            5     10     100
            6     30     300
            7     40     400")

df_in$count = 1L

col_names <- names(df_in)

for( c in 2:length(col_names)-1){

    m = dcast(df_in, paste( 'row', '~', col_names[c]), value.var="count", fill=0L)

    uniq <- unlist(unique(df_in[,get(col_names[c])]))
    newNames <- toString(paste0(col_names[c],'_',uniq))

    m$row <- NULL

    if( c==2 ){
        df_out <- m
        hdrs <- newNames
    }
    else if( c>2 ){
        df_out <- cbind(df_out,m)
        hdrs <- paste(hdrs,newNames,sep=", ")
    }
}

colnames(df_out) <- unlist(strsplit(hdrs, split=", "))

所有答案都是适当且可用的,因此最好的答案是最快的初始响应。 再次感谢你的帮助!!

我经常这样做。 这是我用来创建假人的方法。 非常快。

## reading in your example data
df <- read.table(file = "clipboard", header=TRUE)
df$row <- NULL

uniq <- unique(df$values)
m <- matrix(0, nrow(df), length(uniq), dimnames = list(NULL, paste0("column_", uniq)))

for (i in seq_along(df$values)) {
  k <- match(df$values[i], uniq, 0)
  m[i,k] <- 1
}

结果:

> m
     column_10 column_20 column_30 column_40
[1,]         1         0         0         0
[2,]         0         1         0         0
[3,]         0         0         1         0
[4,]         0         0         0         1
[5,]         1         0         0         0
[6,]         0         0         1         0
[7,]         0         0         0         1

通过使用矩阵索引矩阵来避免循环的另一个变体:

m[cbind(seq.int(nrow(m)), match(df$values, uniq))] <- 1

使用table

df1  <- read.table(text="row values
  1     10
  2     20
  3     30
  4     40
  5     10
  6     30
  7     40", header=TRUE, stringsAsFactors=FALSE)

  table(df1)

    values
row 10 20 30 40
  1  1  0  0  0
  2  0  1  0  0
  3  0  0  1  0
  4  0  0  0  1
  5  1  0  0  0
  6  0  0  1  0
  7  0  0  0  1

您可以像这样对table索引:

table(df1)[5,1]
[1] 1

编辑要回答您的其他请求,您可以像这样创建新的列名:

tbl <-table(df1)
out<-as.data.frame.matrix(tbl) #to transform into a data.frame
colnames(out) <-make.names(colnames(out)) #to make new column names
out
  X10 X20 X30 X40
1   1   0   0   0
2   0   1   0   0
3   0   0   1   0
4   0   0   0   1
5   1   0   0   0
6   0   0   1   0
7   0   0   0   1

这是一个data.table解决方案。 我首先添加一个count列,然后使用dcast()dcast()为宽格式。 顺便说一下,这足够快以用于具有1000万或更多行的数据。

library(data.table)

tab = fread("row values
  1     10
  2     20
  3     30
  4     40
  5     10
  6     30
  7     40")

tab$count = 1L

res = dcast(tab, row ~ values, value.var="count", fill=0L)
res
#    row 10 20 30 40
# 1:   1  1  0  0  0
# 2:   2  0  1  0  0
# 3:   3  0  0  1  0
# 4:   4  0  0  0  1
# 5:   5  1  0  0  0
# 6:   6  0  0  1  0
# 7:   7  0  0  0  1

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM