【问题标题】:converting rows to columns of a data frame in R在R中将行转换为数据框的列
【发布时间】:2016-09-19 03:55:34
【问题描述】:

我有一个这样的数据集

movieID    title   year   country    genre  directorName    Rating     actorName1     actorName.2
1          hello    1995    USA      action    john smith       6        tom hanks      charlie sheen
2          MI2      1997    USA      action    mad max          8        tom cruize     some_body
3          MI2      1997    USA      thriller  mad max          8        tom cruize     some_body

基本上有很多行只有不同的用户给定流派,我希望列具有流派1、流派2……

我尝试了 reshape() 但它只会根据一些 ID 变量进行转换。如果有人有任何想法,请告诉我

【问题讨论】:

    标签: r dataframe reshape


    【解决方案1】:

    用 dplyr 和 tidyr 试试这个:

    library(tidyr)
    library(dplyr)
    df %>% mutate(yesno=1) %>% spread(genre, yesno, fill=0)
    

    这会创建一个 yesno 列,它只为每个流派提供一个要填写的值。然后我们可以使用来自 tidyr 的传播。 fill=0 表示用 0 而不是 NA 填充不属于该流派的。

    之前:

         genre         title yesno
    1   action lethal weapon     1
    2 thriller       shining     1
    3   action         taken     1
    4    scifi         alien     1
    

    之后:

              title action scifi thriller
    1         alien      0     1        0
    2 lethal weapon      1     0        0
    3       shining      0     0        1
    4         taken      1     0        0
    

    【讨论】:

    • 我可以将它作为不同的列,如流派1、流派2、流派3。我不介意像genre5这样的最后几列是NA
    • 有什么不同的列?每种类型都是不同的列。
    • 这会增加我的列数,因为有 23 种独特的流派。有什么解决方法吗?
    • 我认为这就是你想要的。你在用这些列做什么?如果不是特定类型,genre1 会代表什么?
    【解决方案2】:

    如果您了解reshape() 查看数据的镜头,您可以使用reshape() 来执行此操作。


    背景

    首先,在数据管理relational model 的上下文中考虑记录 的概念。一般来说,在一张数据表中,每条记录都应该对应一个定义明确的数据单元,简称为记录单元,一个或多个列作为identificationkey 变量,用于区分记录单元的唯一实例。

    通常,单位由一组标量变量描述。换句话说,每条记录都与一个或多个标量值相关联,每个标量值都提供有关该单元的一条信息。在一个不错的简单世界中,单位的 all 属性将是标量,因此您可以将每个变量表示为单个列向量,每个元素/单元格对应一个记录单元,从而提供值该特定单元的特定属性。


    除了属性的概念之外,识别单元的类型分组分类是可能的并且非常常见。这些通常表示为单位的附加标量属性。

    当人们谈论表格数据的长格式宽格式时,他们通常指的是这些类型分类在表格中的布局方式.这种数据布局的选择与表中由单个记录表示的单位的选择直接相关。这些实际上是同一个选择。

    例如,在对每个个体进行多次测量的实验中,可以为每个记录存储一个测量值,其中个体代表多个记录,并使用类型列来区分测量类型。或者,可以为每条记录存储一个个体,每个测量值由单个列表示。相对而言,前一种格式长,后一种格式宽。但是现在考虑一下,如果每个人都属于实验中的一个实验组,则可以为每个记录存储一个组,每个人由一组列表示,每个测量值由该组中的一个列表示。如果您愿意,这仍然是一种“更广泛”的格式。都是相对的。


    不幸的是,单位特征有时比简单的标量值更复杂。最常见的情况是多值属性,有时被描述为多对一关系(尤其是在 DBMS 的上下文中)。换句话说,属性的多个值可以与单个记录单元相关联。在这种情况下,不可能将多值属性表示为数据集中的简单列向量。程序员在尝试处理这种复杂性时经常会遇到一些技巧,例如:

    • 将多个值连接成单个标量值(例如单个逗号分隔的字符串或位向量)。我们称之为“串联黑客”。
    • 为属性的每个值复制单元记录。 (这通常只有在数据集中只有一个属性是多值的情况下才合理。)我们称之为“复制黑客”。
    • 将属性分成多个自身的“实例”,每个“实例”存储在自己的列中。我们称之为“分离黑客”。
    • 只是尝试忽略多个值中的一个以外的所有值。我们称之为“无知黑客”。

    在某些情况下,可以使用特殊数据类型更恰当地将数据表示为伪列向量。例如,PostgreSQL 提供了array column type,甚至 R data.frames 也可以具有列表列,其各个元素可以保存 R 支持的任何数据类型,包括多元素向量。这些表示通常比上述黑客更可取。

    可能最广泛使用的解决方案是我不认为是 hack 将多值属性与主数据表完全分离,而是将其存储为一个单独的表,该表链接到一个键上的主表.辅助表中的每条记录都有一个主表中记录的键,并与键一起存储多值属性的单个值。这就是关系模型所提倡的设计。

    当然,这些方法都有自己的权衡取舍,对于特定情况的最佳分析可能非常复杂、模糊,甚至有些主观。此处不再赘述。


    在我开始谈论reshape() 之前,需要强调的是,单位类型与多值属性完全不同。重塑数据通常应该是关于管理打字和记录单元选择。它不应该是关于管理多值属性布局,但它可以以这种方式使用,正如我们将看到的那样。


    reshape()

    在最抽象的情况下,reshape() 可用于将一组类型化的标量数据列从每个类型的一个转换为具有鉴别器列 到每个类型的一个 ,列名中带有 鉴别符后缀,对于每个唯一(可能是多列),反之亦然反之亦然。

    使用前面介绍的术语,键通常与单个记录单元相对应。每个键唯一标识一个记录单元。

    数据列是描述记录单元的实际变量/属性,鉴别器用于区分不同类型的数据变量。

    reshape() 文档和接口的术语中,关键列是“id”列,鉴别器是“时间”列,数据列是“变化”列。

    请务必了解,您指定为 idvar 参数的键始终是 wide 格式的唯一键,无论您是从 转换为 宽long, 或 to long from 宽。在长格式中,唯一键是idvar加上鉴别器列 (timevar)。

    这是一个简单的演示:

    ## define example long table
    long <- data.frame(id1=rep(letters[1:2],each=4L),id2=rep(1:2,each=2L),type=1:2,x=1:8,y=9:16);
    long;
    ##   id1 id2 type x  y
    ## 1   a   1    1 1  9
    ## 2   a   1    2 2 10
    ## 3   a   2    1 3 11
    ## 4   a   2    2 4 12
    ## 5   b   1    1 5 13
    ## 6   b   1    2 6 14
    ## 7   b   2    1 7 15
    ## 8   b   2    2 8 16
    
    ## convert to wide
    idvar <- c('id1','id2');
    timevar <- 'type';
    wide <- reshape(long,dir='w',idvar=idvar,timevar=timevar);
    attr(wide,'reshapeWide') <- NULL; ## remove "helper" attribute, which cannot always be relied upon
    wide;
    ##   id1 id2 x.1 y.1 x.2 y.2
    ## 1   a   1   1   9   2  10
    ## 3   a   2   3  11   4  12
    ## 5   b   1   5  13   6  14
    ## 7   b   2   7  15   8  16
    
    ## convert back to long
    long2 <- reshape(wide,dir='l',idvar=idvar,timevar=timevar,varying=names(wide)[!names(wide)%in%c(idvar,timevar)]);
    attr(long2,'reshapeLong') <- NULL; ## remove "helper" attribute, which cannot always be relied upon
    long2 <- long2[do.call(order,long2[c(idvar,timevar)]),]; ## better order, corresponding with original long
    rownames(long2) <- NULL; ## remove useless row names
    long2$type <- as.integer(long2$type); ## annoyingly, longifying interprets discriminator suffixes as doubles
    identical(long,long2);
    ## [1] TRUE
    

    上面的代码还演示了reshape() 所犯的一些怪癖,例如属性分配(我从未见过有人依赖过)、意外的行顺序、不需要的行名和不理想的向量类型推导。如上所示,所有这些怪癖都可以通过简单的修改来掩盖。

    还要注意varying参数在long到wide转换时可以省略,这种情况下reshape()通过消元过程推导出来,但是在wide到long转换时不能省略。


    输入

    您遇到的情况似乎是您有一个 假定 每部电影包含一行的 data.frame,但每个电影记录已针对每种类型复制与电影有关。换句话说,movie 是记录单元,genre 是与电影相关的多值属性,目前由复制黑客表示。

    您的目标似乎是将数据从重复破解转换为分离破解。

    我并不是说在这里听起来太挑剔了;这些黑客被广泛使用,并且在许多情况下,以相对简单的方式处理这种复杂性相当有效。这很可能是您的应用程序的一个很好的解决方案。但我要直言不讳;这些都是黑客,远非最合适或最强大的数据处理解决方案。而且我同意分离黑客比复制黑客更好。


    另一个令人困惑的细节是movieID 列似乎每行唯一,而不是每部电影唯一。 ID 2 和 3 似乎都与电影 MI2 相关。

    我的解释是,在输入中,由于复制黑客已被用于处理多种流派,因此每一行可以被认为是每个 流派实例 唯一的。换句话说,每一行代表单个电影中使用的流派的单个实例。因此,movieID 列最好被认为是一个流派实例标识符,只是被错误命名了。 (另一种解释是它生成不正确,并且每部电影应该是唯一的,在这种情况下,它应该被修复并与后面描述的关键列相同。)


    解决方案

    我们可以通过调用reshape()从长格式转换为宽格式来解决这个问题。

    回想一下,重塑应该用于类型布局,用于在记录单元表示之间导航。在这里,我们将使用它来转换当前存储在 genre 列中的多值属性的布局方式。

    现在,最重要的问题是,哪些列是键(idvar),哪些是鉴别器(timevar),哪些是数据(varying)?

    最简单的是genre 列。这是一个数据列。它不是帮助唯一识别宽格式中每条电影记录的密钥的一部分,当然也不是其他数据列的鉴别器,因此它本身必须是一个数据列。我们也可以通过考虑在转换过程中必须发生什么来得出这个答案;对于每个唯一键,流派值必须从每个值一行到每个值一列,这是从长到宽转换时所有数据列都会发生的情况。

    现在考虑鉴别器列很有用。哪一个?实际上,它不存在于输入中。没有列说“这是类型 X,这是类型 Y”。那么我们该怎么办?根据您所需的输出,您希望将每个流派与一个顺序索引号相关联,大概是按行顺序。这意味着在将 data.frame 传递给reshape() 时,我们需要用这样的序列合成一个新列。但是,我们必须小心确保序列为每部电影重新开始,否则输入表中的每条记录都会看到其流派在输出中占据其自己的列,因为它具有唯一的鉴别器后缀。我们可以使用ave()(按键列分组)和transform() 来做到这一点。如果您不指定timevar 参数,我们将把合成列命名为time,这是reshape() 的默认假设。这将允许我们省略该参数的说明。 (注意:我一直希望reshape() 会默认使用这样的行顺序序列,而不是查找名为time 的输入列,但它没有这样做。哦,好吧。)

    现在让我们处理movieID 列。作为输入表中的唯一标识符,将其包含在输出表中的唯一方法是将其也视为数据列,以便鉴别器将其拆分为单独的列。我决定假设您不想这样做,所以我只是在重塑之前将其从输入表中删除,方法是利用相同的 transform() 调用。如果需要,您可以切除删除部分以查看在转换中包含 movieID 的效果。

    剩下的列是 titleyearcountrydirectorNameRatingactorName1actorName.2。我们应该如何对待这些?

    从技术上讲,从概念上讲,大部分应该是数据列。它们不能成为鉴别器(我们已经介绍过),而且它们中的大多数(例如Rating)不可能被视为关键列。同样,从概念上讲。

    但是将它们中的任何一个指定为数据列是不正确的。原因是我们没有以正常方式使用reshape()。我们知道电影记录已经被输入 data.frame 使用的genre 复制黑客复制了,所以我刚刚列出的所有列实际上只是电影记录组中的重复项。我们需要这些列有效地折叠到输出中的单个记录,而这正是通过reshape() 调用的关键列所发生的情况。因此,我们必须通过将它们传递给 idvar 参数来将它们全部标识为键列。

    另一种思考方式是,除了重复数据删除(如果从长到宽)或重复(如果从宽到长)之外,reshape() 不会触及关键列。只有鉴别器列从列传输到后缀(如果从长到宽)或反之亦然(如果从宽到长),并且数据列从单列传输到多列(如果从长到宽)或反之亦然(如果从宽到长)。除了重复数据删除之外,我们需要这些列保持不变。因此,除了目标多值属性列genre 和合成的time 列(在这种情况下,还有无关的movieID 列)之外,我们需要将所有 列指定为键列.

    请注意,即使一个或多个键列可以作为电影记录的真正键也是如此。例如,如果已知title 在电影表中是唯一的,那么仅将title 指定为键以及我列出的所有其他列名作为数据列仍然是不正确的,因为它们将是尽管我们知道每个电影记录组中的所有值都是相同的,但根据合成鉴别器在输出中加宽。

    所以,这是最终结果:

    df <- data.frame(movieID=c(1L,2L,3L),title=c('hello','MI2','MI2'),year=c(1995L,1997L,1997L),country=c('USA','USA','USA'),genre=c('action','action','thriller'),directorName=c('john smith','mad max','mad max'),Rating=c(6L,8L,8L),actorName1=c('tom hanks','tom cruize','tom cruize'),actorName.2=c('charlie sheen','some_body','some_body'),stringsAsFactors=F);
    idcns <- names(df)[!names(df)%in%c('movieID','genre')];
    reshape(transform(df,movieID=NULL,time=ave(df$movieID,df[idcns],FUN=seq_along)),dir='w',idvar=idcns,sep='');
    ##   title year country directorName Rating actorName1   actorName.2 genre1   genre2
    ## 1 hello 1995     USA   john smith      6  tom hanks charlie sheen action     <NA>
    ## 2   MI2 1997     USA      mad max      8 tom cruize     some_body action thriller
    

    请注意,将哪个向量作为第一个参数传递给ave() 并不相关,因为seq_along() 忽略了它的参数,除了它的长度。但是我们确实需要一个整数向量,因为ave() 试图将其结果强制转换为与参数相同的类型。可以使用df$movieID,因为它是一个整数向量;或者我们可以使用df$yeardf$Rating,或者用seq_len(nrow(df))integer(nrow(df))合成一个整数向量。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-03-05
      • 1970-01-01
      • 2018-02-21
      • 1970-01-01
      • 2017-07-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多