2017-08-09 8 views
4

カンマを追加します。それ以外の場合は、カンマで区切られます。たとえば、数字1, 2, 3, 5, 6, 8, 9, 10, 11 and 131-3,5,6,8-11,13となります。シーケンシャル引用:シーケンシャル場合、ハイフンで別々の番号 - 私は、彼らが連続している場合の数字は、ハイフンで区切る必要がありR.の図のため<a href="https://writing.wisc.edu/Handbook/DocCSE_CitationSystems.html" rel="nofollow noreferrer">sequential citation numbers</a>を生成する場合ではない

この質問はpreviously answered for c#されている、と私はRのために働く機能を書かれているが、この機能を向上させることができます。私はこの質問を、同様の必要性を持つかもしれない他人のための参考資料として投稿します。 Rについても同様の質問がある場合は(私がしなかった)、投票を終了して質問を削除してください。

以下の機能は非常にエレガントではありませんが、仕事をするようです。 どのように機能を短く、よりエレガントにするには?

x <- c(1,2,3,5,6,8,9,10,11,13) 

library(zoo) ## the function requires zoo::na.approx function 

##' @title Generate hyphenated sequential citation from an integer vector 
##' @param x integer vector giving citation or page numbers 
##' @importFrom zoo na.approx 

seq.citation <- function(x) { 

## Result if lenght of the integer vector is 1. 
if(length(x) == 1) return(x) else { 

## Sort 
x <- sort(x) 

## Difference 
df <- diff(x) 

## Index to determine start and end points 
ind <- c("start", rep("no", length(df)-1), "end") 
ind[which(df > 1)] <- "end" 

## Temporary start point vector 
sts <- which(ind == "end") + 1 
ind[sts[sts < length(ind)]] <- "start" 

## Replace the first index element 
ind[1] <- "start" 

## Replace the last index element, if preceding one is "end" 
if(ind[length(ind)-1] == "end") ind[length(ind)] <- "start" 

## Groups for comma separation using "start" as the determining value. 
grp <- rep(NA, length(x)) 
grp[which(ind == "start")] <- 1:length(grp[which(ind == "start")]) 
grp <- zoo::na.approx(grp, method = "constant", rule = 2) 

## Split sequences by group 
seqs <- split(x, grp) 

seqs <- lapply(seqs, function(k) { 
    if(length(k) == 1) k else { 
    if(length(k) == 2) paste(k[1], k[2], sep = ",") else { 
    paste(k[1], k[length(k)], sep = "-") 
    }} 
}) 

## Result 
return(do.call("paste", c(seqs, sep = ","))) 
} 
} 

seq.citation(x) 
# [1] "1-3,5,6,8-11,13" 
+2

参照、また、同様の[ポスト](https://stackoverflow.com/questions/34636461/function-to-summarize -vector-of-a-a-string)数字 –

答えて

6

あなたはこれがあなたの例のために働くとかなり一般的であるべきtapply

paste(tapply(x, cumsum(c(1, diff(x) != 1)), function(i) 
    ifelse(length(i) > 2, paste0(head(i, 1), '-', tail(i, 1)), 
          paste(i, collapse = ','))), collapse = ',') 

[1] "1-3,5,6,8-11,13" 
+1

が更新されました。見た目 – Sotos

+1

は「エレガント」と「ショート」の要件を満たし、私の実際のデータセットのために働く必要があります。ありがとう!結果に 'as.character()'を追加して、長さ1の要素も文字として返すことができます。 – Mikko

4

を使用してベースRを経由して簡単に行うことができます。

# get run lengths of differences, with max value of 2 
r <- rle(c(1, pmin(diff(x), 2))) 

# paste selected x values with appropriate separator 
res <- paste0(x[c(1, cumsum(r$lengths))], c("-", ",")[r$values], collapse="") 

# drop final character, which is a separator 
res <- substr(res, 1, nchar(res)-1) 

これは、もちろん、あり

res 
[1] "1-3,5-6,8-11,13" 
+0

@sotosは 'GSUBの追加( " - \\ D + - "、 " - "、RES)を'、あなたはほとんど、行ってもいいはずです。 – lmo

+0

何らかの理由で、私の正規表現 'gsub'は8-10-11で動作しません。私は理由を理解するのに十分なコーヒーを持っていない。 – lmo

+0

ニース!この解決策は、最初の要素の長さが1であれば、 'res'の先頭に1-1を返します。何とか固定する必要があります。また、それぞれの 'res'の最後にカンマをつけています。 – Mikko

1

、 "R.utils" パッケージからseqToHumanReadable機能を返します。

library(R.utils) 
seqToHumanReadable(x) 
# [1] "1-3, 5, 6, 8-11, 13" 
seqToHumanReadable(x, tau = 1) ## If you want 5-6 and not 5, 6 
# [1] "1-3, 5-6, 8-11, 13" 

結果の外観を制御することもできる。

seqToHumanReadable(x, delimiter = "...", collapse = " | ") 
# [1] "1...3 | 5 | 6 | 8...11 | 13" 
関連する問題