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

时间:2017-03-13 18:17:35

标签: r machine-learning data.table

我的每个数据列都将重新调整并放入0到100的bin中.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多个源列?

EDIT2

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

# 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=", "))

所有答案都是适当且可用的,因此最快的答案是最快的答案。再次感谢您的帮助!!

3 个答案:

答案 0 :(得分:2)

我经常这样做。这是我用来制作假人的方法。它非常快。

## 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

答案 1 :(得分: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

答案 2 :(得分:1)

这是一个data.table解决方案。我首先添加count列,然后使用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