2017-04-14 8 views
1

これは私のデータフレームの例です。私はRで働いています。データフレームに対応するシーズンの列を追加する

date   name  count 
2016-11-12 Joe   5 
2016-11-15 Bob   5 
2016-06-15 Nick  12 
2016-10-16 Cate  6 

日付に対応する季節を教えてくれるデータフレームに列を追加したいと思います。私はそれがこのようになりたいと思います:

date   name  count  Season 
2016-11-12 Joe   5   Winter 
2016-11-15 Bob   5   Winter 
2017-06-15 Nick  12   Summer 
2017-10-16 Cate  6   Fall 

私はいくつかのコードを開始しました:

startWinter <- c(month.name[1], month.name[12], month.name[11]) 
startSummer <- c(month.name[5], month.name[6], month.name[7]) 
startSpring <- c(month.name[2], month.name[3], month.name[4]) 

# create a function to find the correct season based on the month 
MonthSeason <- function(Month) { 
    # !is.na() 
# ignores values with NA 
    # match() 
    # returns a vector of the positions of matches 
    # If the starting month matches a spring season, print "Spring". If the starting month matches a summer season, print "Summer" etc. 
    ifelse(!is.na(match(Month, startSpring)), 
     return("spring"), 
     return(ifelse(!is.na(match(Month, startWinter)), 
         "winter", 
         ifelse(!is.na(match(Month, startSummer)), 
           "summer","fall")))) 
} 

このコードは私の月のシーズンを提供します。私が正しい方法でこの問題を起こしているかどうかはわかりません。誰か助けてくれますか? ありがとう!

答えて

2

ありハックのカップルがあり、その利便性は、あなたがmeteorological or astronomical seasonsを使用するかどうかに依存します。私は両方を提供する、私は彼らが十分な柔軟性を提供すると思います。

提供された2番目のデータは、「冬」以上のものを提供するため、使用します。

txt <- "date   name  count 
2016-11-12 Joe   5 
2016-11-15 Bob   5 
2017-06-15 Nick  12 
2017-10-16 Cate  6" 
dat <- read.table(text = txt, header = TRUE, stringsAsFactors = FALSE) 
dat$date <- as.Date(dat$date) 

季節が厳密に月によって定義されている場合、最も速い方法がうまくいきます。

metseasons <- c(
    "01" = "Winter", "02" = "Winter", 
    "03" = "Spring", "04" = "Spring", "05" = "Spring", 
    "06" = "Summer", "07" = "Summer", "08" = "Summer", 
    "09" = "Fall", "10" = "Fall", "11" = "Fall", 
    "12" = "Winter" 
) 
metseasons[format(dat$date, "%m")] 
#  11  11  06  10 
# "Fall" "Fall" "Summer" "Fall" 

あなたがそのような天文季節として停止/月の開始によって定義されていない、あなたの季節のための日付範囲を使用することを選択した場合は、ここでは別の「ハック」はです:

astroseasons <- as.integer(c("0000", "0320", "0620", "0922", "1221", "1232")) 
astroseasons_labels <- c("Winter", "Spring", "Summer", "Fall", "Winter") 

あなたが適切なDateを使用している場合またはPOSIXタイプの場合は、年を含めているため、一般的なものになります。ユリウス暦の日付を使用すると考えられるかもしれませんが、うるう年には異常が生じます。したがって、2月28日は決して季節的な境界ではないという前提で、私は月の日を「数値化」しています。 Rは文字比較をうまく行っても、cutは数字を期待しているので、整数に変換します。

二セーフガード:cutはどちらか右開き(およびクローズ左)または右閉じた我々の2つのブックエンドがを超えて法的な日数をを拡張する必要があり、その後、(左開き)、エルゴであるため、 "0000"および"1232"。ここでも同様に機能する他の手法があります(たとえば、-InfおよびInf、ポスト整数化を使用)。

astroseasons_labels[ cut(as.integer(format(dat$date, "%m%d")), astroseasons, labels = FALSE) ] 
# [1] "Fall" "Fall" "Spring" "Fall" 

天気予報の季節とそれ以外の場合は3番目の日付は春です。

この解決法は、南半球または他の季節の好み/考え方を考慮して簡単に調整することができます。

を編集:@Kristofersen's answer(ありがとう)によって、私はベンチマークを調べました。 lubridate::monthPOSIXct-to-POSIXltの変換を使用して月を抽出します。これは私のformat(x, "%m")メソッドよりも10倍以上速くなります。そのように:

metseasons2 <- c(
    "Winter", "Winter", 
    "Spring", "Spring", "Spring", 
    "Summer", "Summer", "Summer", 
    "Fall", "Fall", "Fall", 
    "Winter" 
) 

留意as.POSIXlt戻り0ベースヶ月つまり、私たちは1を追加します。

metseasons2[ 1 + as.POSIXlt(dat$date)$mon ] 
# [1] "Fall" "Fall" "Summer" "Fall" 

比較:

library(lubridate) 
library(microbenchmark) 
set.seed(42) 
x <- Sys.Date() + sample(1e3) 
xlt <- as.POSIXlt(x) 

microbenchmark(
    metfmt = metseasons[ format(x, "%m") ], 
    metlt = metseasons2[ 1 + xlt$mon ], 
    astrofmt = astroseasons_labels[ cut(as.integer(format(x, "%m%d")), astroseasons, labels = FALSE) ], 
    astrolt = astroseasons_labels[ cut(100*(1+xlt$mon) + xlt$mday, astroseasons, labels = FALSE) ], 
    lubridate = sapply(month(x), seasons) 
) 
# Unit: microseconds 
#  expr  min  lq  mean median  uq  max neval 
#  metfmt 1952.091 2135.157 2289.63943 2212.1025 2308.1945 3748.832 100 
#  metlt 14.223 16.411 22.51550 20.0575 24.7980 68.924 100 
# astrofmt 2240.547 2454.245 2622.73109 2507.8520 2674.5080 3923.874 100 
# astrolt 42.303 54.702 72.98619 66.1885 89.7095 163.373 100 
# lubridate 5906.963 6473.298 7018.11535 6783.2700 7508.0565 11474.050 100 

のでas.POSIXlt(...)$monを用いる方法が大幅に高速化されています。 (@ Kristofersenの答えは、ifelseでベクトル化することで改善することができますが、cutの有無にかかわらず、ベクトルルックアップの速度とはまだ比較されません)。

1

データがDFの場合:

# create dataframe for month and corresponding season 
dfSeason <- data.frame(season = c(rep("Winter", 3), rep("Summer", 3), 
rep("Spring", 3), rep("Fall", 3)), 
        month = month.name[c(11,12,1, 5:7, 2:4, 8:10)], 
        stringsAsFactors = F) 

# make date as date 
df$data <- as.Date(df$date) 

# match the month of the date in df (format %B) with month in season 
# then use it to index the season of dfSeason 
df$season <- dfSeason$season[match(format(df$data, "%B"), dfSeason$month)] 
+0

ありがとうございます。これを実装しようとしたとき、シーズンの列にすべてのNAがありました。なぜこれができるのか知っていますか? – Amanda

+0

私の悪いことに、 'match'の' dfSeason'は 'dfSeason $ month'です – din

1

これをRubridateと関数月の数字をシーズンに変更する。

library(lubridate) 

seasons = function(x){ 
    if(x %in% 2:4) return("Spring") 
    if(x %in% 5:7) return("Summer") 
    if(x %in% 8:10) return("Fall") 
    if(x %in% c(11,12,1)) return("Winter") 

} 

dat$Season = sapply(month(dat$date), seasons) 

> dat 
     date name count Season 
1 2016-11-12 Joe  5 Winter 
2 2016-11-15 Bob  5 Winter 
3 2016-06-15 Nick 12 Summer 
4 2016-10-16 Cate  6 Fall 
関連する問題