2017-07-28 4 views
1

は私もStackexchangeで見つかったアルゴリズム(、素晴らしい仕事の連中を計算平日に基づいて式を書いたここでは、コードスニペットです:。2つのPOSIXct日付配列の間の特定の曜日を計算し、別の数値配列を返す方法は?

countwd <- function(start, end, day){ 
    x <- seq(start, end, by=1) 
    y <- weekdays(x, TRUE) 
    sum(y==day) 
} 
x$OFFDAY <- NULL 
for(i in 1:nrow(x)){ 
    x$OFFDAY[i] <- countwd(x$PICK_DATE[i], x$SHIP_DATE[i], "Mon") 
} 

これは(あまりにも遅いですが、あたり2〜4行のようなループが進みます第二!!!!)、と私は月ごとに数百万のエントリを持っている。ここ

は、関数のベクトル化である:。

x$OFFDAY <- countwd(x$PICK_DATE, x$SHIP_DATE, "Mon") 

は、このエラーを表示します

Error in seq.POSIXt(start, end, by = 1) : 'from' must be of length 1

私は比較する2つのベクトルを持っているので、この場合の「適用」ファミリ関数を適用する方法を理解できません(これは本当に新しいです)。

サンプルデータ:

PICK_DATE SHIP_DATE 
01-APR-2017 00:51 02-APR-2017 06:55 AM 
01-APR-2017 00:51 02-APR-2017 12:11 PM 
01-APR-2017 00:51 02-APR-2017 12:11 PM 
01-APR-2017 00:51 02-APR-2017 09:39 AM 

(。しかし第2の値を返し、全く分からなぜしかし、私はそれを回避することはできません)私はPOSIXctにこれらを変換した、と式は、個々の値に適しています:

>countwd(x$PICK_DATE[1], x$SHIP_DATE[1], "Mon") 
[1] 0 
+0

複数のリストまたはベクトル引数に関数を適用するには、 'mapply'を使用する必要があります。ここで 'x $ OFFDAY < - mapply(FUN = countwd、start = x $ PICK_DATE、end = x $ SHIP_DATE、day =" Mon ")'を実行します。しかし、日付がはるかに離れていれば、まだ遅いかもしれません。おそらく 'lubridate'パッケージを使う方が速いですが、私はそれを考える必要があります。 – meenaparam

+0

'countwd'に個々の値が与えられているとき、これが意図した通りに動作することは確かですか?あなたは1または2を返すケースを試したことがありますか?0ではありませんか? – demirev

+0

はい、 'countwd'は正しいカウントを与えますが、日数は与えません。なぜか分かりませんが、それは数秒です。 – Arani

答えて

1

@ demirevの回答と上記のコメントに基づいて、改良されたcountwd関数とmapplyを使用した実例があります。 lubridateを使用してヘルパー列をいくつか入れて解決策を確認し、一部の日付を0以外のdf$off_daysに戻すように変更しました。

library(lubridate) 

df <- data.frame(pick_date = c(rep("01-APR-2017 00:51", 4)), ship_date = c("05-APR-2017 06:55", "09-APR-2017 12:11", "30-APR-2017 12:11", "02-MAY-2017 12:11")) 

df$pick_date <- lubridate::dmy_hm(df$pick_date) 
df$ship_date <- lubridate::dmy_hm(df$ship_date) 

df$pick_day <- wday(df$pick_date, label = T) 
df$ship_day <- wday(df$ship_date, label = T) 
df$days_between <- interval(df$pick_date, df$ship_date) %/% days() 

countwd <- function(start, end, day) { 
    x <- seq(start, end, by="day") 
    y <- weekdays(x, TRUE) 
    sum(y==day) 
} 

df$off_days <- mapply(countwd, df$pick_date, df$ship_date, "Mon") 
df 

      pick_date   ship_date pick_day ship_day days_between off_days 
1 2017-04-01 00:51:00 2017-04-05 06:55:00  Sat  Wed   4  1 
2 2017-04-01 00:51:00 2017-04-09 12:11:00  Sat  Sun   8  1 
3 2017-04-01 00:51:00 2017-04-30 12:11:00  Sat  Sun   29  4 
4 2017-04-01 00:51:00 2017-05-02 12:11:00  Sat  Tues   31  5 
+0

うん、フォーマットの問題。おそらく、PICK_DATE列をどのように解析するかによって発生します。私は構文解析の前に数式を使用し、それは滑らかになった。私は時の値に応じて時の値を変更するように日付を解析する必要があります。 5時以降にご注文いただいた場合、翌朝9 AMから注文が処理され、リードタイムはそれに応じて計算されます。 – Arani

+0

@Araniよく見ると、 'lubridate'をチェックしてください - このパッケージを使用して時間を一緒に追加する簡単な方法があります。 「日付時間による算術」の節を参照してください。 https://cran.r-project.org/web/packages/lubridate/vignettes/lubridate.html – meenaparam

+0

ありがとうございました。 – Arani

2

複数の様々な入力の関数をベクトル化する簡単な方法は、使用することですmapply

mapply(countwd, x$SHIP_DATE, x$PICK_DATE, "Mon") 

あるいは、あなたは(この方法は、構文はforループに非常によく似ている最初の引数としてsapplyを使用して、インデックスのシーケンスを渡すことができます。

sapply(1:nrow(x), function(i) countwd(x$SHIP_DATE[i], x$PICK_DATE[i], "Mon"))

あなたのケースの主な非効率性がしかし、茎countwd機能から関数にはPOSIXtベクトルを渡していることに注意してください。したがって、seqが関数の最初の行で呼び出されると、by引数は数日ではなく数秒になります。これにより、不必要に大きなベクトルが生成されます(詳細は?seq.POSIXtを参照)。次のようにcountwdを変更

が大幅にパフォーマンスを向上させる必要があります。

countwd <- function(start, end, day) { 
    x <- seq(start, end, by="day") 
    y <- weekdays(x, TRUE) 
    sum(y==day) 
} 

weekdaysはロケール固有であり、あなたのロケール設定に応じて、意図したとおりに動作しない可能性があることに注意してください。

+0

素晴らしい提案。しかし、 'mapply'を使用すると、このエラーが発生します。_ zero-length入力は、長さがゼロ以外のものと混在することはできません。 'sapply'を使用すると、このエラーが発生します。_'from 'は長さ1_でなければなりません。あなたが最初からどのように問題に近づくかを示唆できれば、それは私にとってより良いものでした。 – Arani

+0

@Arani申し訳ありませんが、私は何か良い思いつくことができません。 @demirevが 'countwd'関数に提案した変更は、問題を修正するようです。あなたのデータが正しく書式化されていないため、あなたの 'mapply'が動作していないと思うので、私は完全に作業した例を入れました。 – meenaparam

+0

ええ、ちょうどsomethng奇妙なことが起こっていることがわかりました。データが置き換えられました!私はバックアップを取って、今は 'mapply'と' sapply'の両方でこのエラーを出しています:_error in seq.int(0、to0 - from、by): 'by' argument_で間違ったサインイン。 – Arani

関連する問題