넓은 범위에서 긴 형식으로 data.frame 재구성


164

data.frame넓은 테이블에서 긴 테이블 로 변환하는 데 문제가 있습니다. 현재 다음과 같이 보입니다.

Code Country        1950    1951    1952    1953    1954
AFG  Afghanistan    20,249  21,352  22,532  23,557  24,555
ALB  Albania        8,097   8,986   10,058  11,123  12,246

이제 이것을 이것을 data.frame길게 변환하고 싶습니다 data.frame. 이 같은:

Code Country        Year    Value
AFG  Afghanistan    1950    20,249
AFG  Afghanistan    1951    21,352
AFG  Afghanistan    1952    22,532
AFG  Afghanistan    1953    23,557
AFG  Afghanistan    1954    24,555
ALB  Albania        1950    8,097
ALB  Albania        1951    8,986
ALB  Albania        1952    10,058
ALB  Albania        1953    11,123
ALB  Albania        1954    12,246

나는 일부 사람들이 비슷한 질문에서 제안한 것처럼 melt()reshape()기능을 살펴보고 이미 사용해 보았습니다 . 그러나 지금까지는 지저분한 결과 만 얻었습니다.

가능한 경우 처리하기가 reshape()조금 더 좋기 때문에 함수 로 수행하고 싶습니다 .


2
그래도 문제가 있다면 알고 있지만, 모양 변경 패키지의 기능을 용융 및 주조하다하지 마십시오 (그리고 참 -_-합니다.)
에두아르도 레오니에게

1
그리고 reshape 패키지가 reshape2로 대체되었습니다.
IRTFM 2018 년

5
그리고 이제 reshape2가 tidyr로 대체되었습니다.
drhagen

답변:


93

reshape()melt/ 로 익숙해지기까지 시간이 걸립니다 cast. 다음은 데이터 프레임이 호출되었다고 가정 할 때 모양이 변경된 솔루션입니다 d.

reshape(d, 
        direction = "long",
        varying = list(names(d)[3:7]),
        v.names = "Value",
        idvar = c("Code", "Country"),
        timevar = "Year",
        times = 1950:1954)

153

세 가지 대안 솔루션 :

1) :

패키지 melt에서 와 동일한 기능을 사용할 수 있습니다 reshape2(확장 및 개선 된 구현). meltfrom data.table에는 또한 melt-function from 보다 많은 매개 변수가 있습니다 reshape2. 예를 들어 변수 열의 이름을 지정할 수도 있습니다.

library(data.table)
long <- melt(setDT(wide), id.vars = c("Code","Country"), variable.name = "year")

이것은 다음을 제공합니다.

> long
    Code     Country year  value
 1:  AFG Afghanistan 1950 20,249
 2:  ALB     Albania 1950  8,097
 3:  AFG Afghanistan 1951 21,352
 4:  ALB     Albania 1951  8,986
 5:  AFG Afghanistan 1952 22,532
 6:  ALB     Albania 1952 10,058
 7:  AFG Afghanistan 1953 23,557
 8:  ALB     Albania 1953 11,123
 9:  AFG Afghanistan 1954 24,555
10:  ALB     Albania 1954 12,246

대체 표기법 :

melt(setDT(wide), id.vars = 1:2, variable.name = "year")
melt(setDT(wide), measure.vars = 3:7, variable.name = "year")
melt(setDT(wide), measure.vars = as.character(1950:1954), variable.name = "year")

2) :

library(tidyr)
long <- wide %>% gather(year, value, -c(Code, Country))

대체 표기법 :

wide %>% gather(year, value, -Code, -Country)
wide %>% gather(year, value, -1:-2)
wide %>% gather(year, value, -(1:2))
wide %>% gather(year, value, -1, -2)
wide %>% gather(year, value, 3:7)
wide %>% gather(year, value, `1950`:`1954`)

3) :

library(reshape2)
long <- melt(wide, id.vars = c("Code", "Country"))

동일한 결과를 제공하는 몇 가지 대체 표기법 :

# you can also define the id-variables by column number
melt(wide, id.vars = 1:2)

# as an alternative you can also specify the measure-variables
# all other variables will then be used as id-variables
melt(wide, measure.vars = 3:7)
melt(wide, measure.vars = as.character(1950:1954))

노트:

  • 은퇴했습니다. CRAN을 유지하는 데 필요한 변경 만 수행됩니다. ( 소스 )
  • 제외 할 경우 NA값을, 당신은 추가 할 수 있습니다 na.rm = TRUE받는 사람 melt뿐만 아니라 gather기능.

데이터의 또 다른 문제점은 값이 R에 의해 문자 값으로 읽히는 것입니다 ( ,숫자 의 결과 ). 다음 gsubas.numeric같이 수리 할 수 ​​있습니다 .

long$value <- as.numeric(gsub(",", "", long$value))

또는 직접으로 data.table또는 dplyr:

# data.table
long <- melt(setDT(wide),
             id.vars = c("Code","Country"),
             variable.name = "year")[, value := as.numeric(gsub(",", "", value))]

# tidyr and dplyr
long <- wide %>% gather(year, value, -c(Code,Country)) %>% 
  mutate(value = as.numeric(gsub(",", "", value)))

데이터:

wide <- read.table(text="Code Country        1950    1951    1952    1953    1954
AFG  Afghanistan    20,249  21,352  22,532  23,557  24,555
ALB  Albania        8,097   8,986   10,058  11,123  12,246", header=TRUE, check.names=FALSE)

멋진 대답, 또 하나의 작은 알림 : 이외의 다른 변수에 넣지 마십시오 idtime, 데이터 프레임을 melt이 경우에 수행 할 작업을 말할 수 없습니다.
Jason Goal

1
@JasonGoal 좀 더 자세히 설명해 주시겠습니까? 나는 당신이 의견을 해석하는 것처럼 문제가되지 않아야합니다. id.vars과를 모두 지정 하십시오 measure.vars.
Jaap

나를 위해의 좋은, 모르는 그 id.vars와는 measure.vars첫 번째 대안으로 지정할 수 있습니다, 죄송 혼란에 대한 그 내 잘못.
Jason Goal

이 게시물을 괴로워 미안-누군가 3이 작동 하는지 설명해 줄 수 있습니까? 나는 그것을 테스트하고 작동하지만, 그것이 볼 때 dplyr가 무엇을하고 있는지 이해가 안됩니다 -c(var1, var2)...

1
경우 @ReputableMisnomer tidyr가 보는 -c(var1, var2)긴 포맷 폭의 데이터를 변환하는 경우는 이들 변수를 생략한다.
Jaap

35

사용하여 모양 변경 패키지를 :

#data
x <- read.table(textConnection(
"Code Country        1950    1951    1952    1953    1954
AFG  Afghanistan    20,249  21,352  22,532  23,557  24,555
ALB  Albania        8,097   8,986   10,058  11,123  12,246"), header=TRUE)

library(reshape)

x2 <- melt(x, id = c("Code", "Country"), variable_name = "Year")
x2[,"Year"] <- as.numeric(gsub("X", "" , x2[,"Year"]))

18

으로 tidyr_1.0.0, 또 다른 옵션은pivot_longer

library(tidyr)
pivot_longer(df1, -c(Code, Country), values_to = "Value", names_to = "Year")
# A tibble: 10 x 4
#   Code  Country     Year  Value 
#   <fct> <fct>       <chr> <fct> 
# 1 AFG   Afghanistan 1950  20,249
# 2 AFG   Afghanistan 1951  21,352
# 3 AFG   Afghanistan 1952  22,532
# 4 AFG   Afghanistan 1953  23,557
# 5 AFG   Afghanistan 1954  24,555
# 6 ALB   Albania     1950  8,097 
# 7 ALB   Albania     1951  8,986 
# 8 ALB   Albania     1952  10,058
# 9 ALB   Albania     1953  11,123
#10 ALB   Albania     1954  12,246

데이터

df1 <- structure(list(Code = structure(1:2, .Label = c("AFG", "ALB"), class = "factor"), 
    Country = structure(1:2, .Label = c("Afghanistan", "Albania"
    ), class = "factor"), `1950` = structure(1:2, .Label = c("20,249", 
    "8,097"), class = "factor"), `1951` = structure(1:2, .Label = c("21,352", 
    "8,986"), class = "factor"), `1952` = structure(2:1, .Label = c("10,058", 
    "22,532"), class = "factor"), `1953` = structure(2:1, .Label = c("11,123", 
    "23,557"), class = "factor"), `1954` = structure(2:1, .Label = c("12,246", 
    "24,555"), class = "factor")), class = "data.frame", row.names = c(NA, 
-2L))

1
더 많은 투표가 필요합니다. Tidyverse 블로그 에 따르면 gather은퇴하고 pivot_longer있으며 현재 이것을 달성하는 올바른 방법입니다.
Evan Rosica

16

이 답변은 태그로 설정되어 있기 때문에 , 나는 기초 R의 다른 대안을 공유하는 것이 유용 할 것이라고 생각했다 stack.

그러나 s stack에서는 작동하지 않습니다. is factor경우에만 작동 하며의 설명서에서 다음 을 발견 할 수 있습니다.is.vectorTRUEis.vector

is.vectorTRUEx가 names 이외의 속성이없는 지정된 모드의 벡터이면를 반환 합니다 . FALSE그렇지 않으면 반환 합니다.

@ Jaap 's answer 의 샘플 데이터 사용하고 있는데 , 여기서 연도 열의 값은 factors입니다.

stack접근 방식 은 다음과 같습니다 .

cbind(wide[1:2], stack(lapply(wide[-c(1, 2)], as.character)))
##    Code     Country values  ind
## 1   AFG Afghanistan 20,249 1950
## 2   ALB     Albania  8,097 1950
## 3   AFG Afghanistan 21,352 1951
## 4   ALB     Albania  8,986 1951
## 5   AFG Afghanistan 22,532 1952
## 6   ALB     Albania 10,058 1952
## 7   AFG Afghanistan 23,557 1953
## 8   ALB     Albania 11,123 1953
## 9   AFG Afghanistan 24,555 1954
## 10  ALB     Albania 12,246 1954

11

gatherfrom 사용을 보여주는 또 다른 예는 다음과 같습니다 tidyr. 열을 gather개별적으로 제거하거나 (여기에서와 같이) 원하는 연도를 명시 적으로 포함 하여 열을 선택할 수 있습니다 .

쉼표를 처리하기 위해 (그리고 check.names = FALSE설정되지 않은 경우 X가 추가됨 ) 텍스트 값을 다시 숫자로 변환하기 위해 from 을 사용하여 dplyrmutate를 사용 하고 있습니다. 이것들은 모두의 일부 이므로 함께로드 할 수 있습니다parse_numberreadrtidyverselibrary(tidyverse)

wide %>%
  gather(Year, Value, -Code, -Country) %>%
  mutate(Year = parse_number(Year)
         , Value = parse_number(Value))

보고:

   Code     Country Year Value
1   AFG Afghanistan 1950 20249
2   ALB     Albania 1950  8097
3   AFG Afghanistan 1951 21352
4   ALB     Albania 1951  8986
5   AFG Afghanistan 1952 22532
6   ALB     Albania 1952 10058
7   AFG Afghanistan 1953 23557
8   ALB     Albania 1953 11123
9   AFG Afghanistan 1954 24555
10  ALB     Albania 1954 12246

4

여기에 해결책:

sqldf("Select Code, Country, '1950' As Year, `1950` As Value From wide
        Union All
       Select Code, Country, '1951' As Year, `1951` As Value From wide
        Union All
       Select Code, Country, '1952' As Year, `1952` As Value From wide
        Union All
       Select Code, Country, '1953' As Year, `1953` As Value From wide
        Union All
       Select Code, Country, '1954' As Year, `1954` As Value From wide;")

모든 것을 입력하지 않고 쿼리를하려면 다음을 사용할 수 있습니다.

그것을 구현 한 G. Grothendieck 에게 감사 합니다.

ValCol <- tail(names(wide), -2)

s <- sprintf("Select Code, Country, '%s' As Year, `%s` As Value from wide", ValCol, ValCol)
mquery <- paste(s, collapse = "\n Union All\n")

cat(mquery) #just to show the query
 #> Select Code, Country, '1950' As Year, `1950` As Value from wide
 #>  Union All
 #> Select Code, Country, '1951' As Year, `1951` As Value from wide
 #>  Union All
 #> Select Code, Country, '1952' As Year, `1952` As Value from wide
 #>  Union All
 #> Select Code, Country, '1953' As Year, `1953` As Value from wide
 #>  Union All
 #> Select Code, Country, '1954' As Year, `1954` As Value from wide

sqldf(mquery)
 #>    Code     Country Year  Value
 #> 1   AFG Afghanistan 1950 20,249
 #> 2   ALB     Albania 1950  8,097
 #> 3   AFG Afghanistan 1951 21,352
 #> 4   ALB     Albania 1951  8,986
 #> 5   AFG Afghanistan 1952 22,532
 #> 6   ALB     Albania 1952 10,058
 #> 7   AFG Afghanistan 1953 23,557
 #> 8   ALB     Albania 1953 11,123
 #> 9   AFG Afghanistan 1954 24,555
 #> 10  ALB     Albania 1954 12,246

불행히도, 난 그렇게 생각하지 않습니다 PIVOTUNPIVOT작동 것이다 R SQLite. 보다 정교한 방식으로 쿼리를 작성하려면 다음 게시물을 살펴보십시오.

사용 sprintfSQL 쿼리까지 작성    또는    에 패스 변수를sqldf


0

cdata(변환) 제어 테이블의 개념을 사용하는 패키지를 사용할 수도 있습니다 .

# data
wide <- read.table(text="Code Country        1950    1951    1952    1953    1954
AFG  Afghanistan    20,249  21,352  22,532  23,557  24,555
ALB  Albania        8,097   8,986   10,058  11,123  12,246", header=TRUE, check.names=FALSE)

library(cdata)
# build control table
drec <- data.frame(
    Year=as.character(1950:1954),
    Value=as.character(1950:1954),
    stringsAsFactors=FALSE
)
drec <- cdata::rowrecs_to_blocks_spec(drec, recordKeys=c("Code", "Country"))

# apply control table
cdata::layout_by(drec, wide)

나는 현재 그 패키지를 탐색하고 있으며 접근하기가 쉽다는 것을 알았습니다. 훨씬 복잡한 변환을 위해 설계되었으며 역변환을 포함합니다. 이 튜토리얼 이 없습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.