Cara menghitung nilai TRUE dalam vektor logis

160

Dalam R, apa cara paling efisien / idiomatis untuk menghitung jumlah TRUEnilai dalam vektor logis? Saya dapat memikirkan dua cara:

z <- sample(c(TRUE, FALSE), 1000, rep = TRUE)
sum(z)
# [1] 498

table(z)["TRUE"]
# TRUE 
#  498 

Yang mana yang Anda sukai? Apakah ada yang lebih baik?

Jyotirmoy Bhattacharya
sumber

Jawaban:

174

Ada beberapa masalah ketika vektor logis berisi NAnilai.
Lihat misalnya:

z <- c(TRUE, FALSE, NA)
sum(z) # gives you NA
table(z)["TRUE"] # gives you 1
length(z[z == TRUE]) # f3lix answer, gives you 2 (because NA indexing returns values)

Jadi saya pikir yang paling aman adalah menggunakan na.rm = TRUE:

sum(z, na.rm = TRUE) # best way to count TRUE values

(yang memberi 1). Saya pikir tablesolusi itu kurang efisien (lihat kode tablefungsi).

Juga, Anda harus berhati-hati dengan solusi "tabel", jika tidak ada nilai yang BENAR dalam vektor logis. Misalkan z <- c(NA, FALSE, NA)atau sederhananya z <- c(FALSE, FALSE), lalu table(z)["TRUE"]memberi Anda NAuntuk kedua kasus.

Marek
sumber
table(c(FALSE))["TRUE"]memberi NA, bukan 0.
Yossi Farjoun
@YossiFarjoun Ya, dan itu ada dalam jawaban saya. Ini adalah contoh mengapa itu tidak akan berhasil. Solusi saya adalahsum(z, na.rm = TRUE)
Marek
84

Opsi lain yang belum disebutkan adalah menggunakan which:

length(which(z))

Hanya untuk benar-benar memberikan beberapa konteks pada "pertanyaan yang lebih cepat", selalu termudah hanya untuk menguji diri sendiri. Saya membuat vektor lebih besar untuk perbandingan:

z <- sample(c(TRUE,FALSE),1000000,rep=TRUE)
system.time(sum(z))
   user  system elapsed 
   0.03    0.00    0.03
system.time(length(z[z==TRUE]))
   user  system elapsed 
   0.75    0.07    0.83 
system.time(length(which(z)))
   user  system elapsed 
   1.34    0.28    1.64 
system.time(table(z)["TRUE"])
   user  system elapsed 
  10.62    0.52   11.19 

Jadi jelas menggunakan sumadalah pendekatan terbaik dalam hal ini. Anda mungkin juga ingin memeriksa NAnilai - nilai seperti yang disarankan Marek.

Hanya untuk menambahkan catatan tentang nilai NA dan whichfungsinya:

> which(c(T, F, NA, NULL, T, F))
[1] 1 4
> which(!c(T, F, NA, NULL, T, F))
[1] 2 5

Perhatikan bahwa yang hanya memeriksa logis TRUE, jadi pada dasarnya mengabaikan nilai-nilai non-logis.

Shane
sumber
BTW, ada trik yang menyenangkan dengan pengaturan waktu dalam jawaban Dirk: stackoverflow.com/questions/1748590/revolution-for-r/…
Marek
12

Cara lain adalah

> length(z[z==TRUE])
[1] 498

Meskipun sum(z) bagus dan pendek, bagi saya length(z[z==TRUE])lebih menjelaskan diri sendiri. Padahal, saya pikir dengan tugas sederhana seperti ini tidak benar-benar membuat perbedaan ...

Jika ini adalah vektor besar, Anda mungkin harus menggunakan solusi tercepat, yaitu sum(z). length(z[z==TRUE])sekitar 10x lebih lambat dan table(z)[TRUE]sekitar 200x lebih lambat dari sum(z).

Kesimpulannya, sum(z)adalah yang tercepat untuk mengetik dan mengeksekusi.

f3lix
sumber
6

whichadalah alternatif yang baik, terutama ketika Anda beroperasi pada matriks (periksa ?whichdan perhatikan arr.indargumennya). Tapi saya sarankan Anda tetap dengan sum, karena na.rmargumen yang dapat menangani NAdalam vektor logis. Misalnya:

# create dummy variable
set.seed(100)
x <- round(runif(100, 0, 1))
x <- x == 1
# create NA's
x[seq(1, length(x), 7)] <- NA

Jika Anda ketik sum(x)Anda akan mendapatkan NAakibatnya, tetapi jika Anda lulus na.rm = TRUEdi sumfungsi, Anda akan mendapatkan hasil yang Anda inginkan.

> sum(x)
[1] NA
> sum(x, na.rm=TRUE)
[1] 43

Apakah pertanyaan Anda benar-benar teoretis, atau Anda memiliki masalah praktis tentang vektor-vektor logis?

aL3xa
sumber
Saya mencoba untuk menilai kuis. Melakukan sesuatu seperti penjumlahan (youranswer == rightanswer) dalam suatu aplikasi.
Jyotirmoy Bhattacharya
Balasan saya terlalu lama, jadi saya memposting jawaban baru, karena berbeda dari yang sebelumnya.
aL3xa
6

Pilihan lain adalah menggunakan fungsi ringkasan. Ini memberikan ringkasan dari Ts, Fs dan NAS.

> summary(hival)
   Mode   FALSE    TRUE    NA's 
logical    4367      53    2076 
> 
ramrad
sumber
1
Selanjutnya, untuk mendapatkan hanya hasil "TRUE" (yang akan ditampilkan sebagai string, tetapi juga termasuk "TRUE" dalam output) summary(hival)["TRUE"]:;
michael
0

Saya telah melakukan sesuatu yang serupa beberapa minggu yang lalu. Inilah solusi yang mungkin, ini ditulis dari awal, jadi ini semacam rilis beta atau semacamnya. Saya akan mencoba memperbaikinya dengan menghapus loop dari kode ...

Gagasan utamanya adalah menulis fungsi yang akan mengambil 2 (atau 3) argumen. Yang pertama adalah data.frameyang menyimpan data yang dikumpulkan dari kuesioner, dan yang kedua adalah vektor numerik dengan jawaban yang benar (ini hanya berlaku untuk kuesioner pilihan tunggal). Atau, Anda bisa menambahkan argumen ketiga yang akan mengembalikan vektor numerik dengan skor akhir, atau data.frame dengan skor tertanam.

fscore <- function(x, sol, output = 'numeric') {
    if (ncol(x) != length(sol)) {
        stop('Number of items differs from length of correct answers!')
    } else {
        inc <- matrix(ncol=ncol(x), nrow=nrow(x))
        for (i in 1:ncol(x)) {
            inc[,i] <- x[,i] == sol[i]
        }
        if (output == 'numeric') {
            res <- rowSums(inc)
        } else if (output == 'data.frame') {
            res <- data.frame(x, result = rowSums(inc))
        } else {
            stop('Type not supported!')
        }
    }
    return(res)
}

Saya akan mencoba melakukan ini dengan cara yang lebih elegan dengan beberapa fungsi * ply. Perhatikan bahwa saya tidak mengajukan na.rmargumen ... Akan melakukan itu

# create dummy data frame - values from 1 to 5
set.seed(100)
d <- as.data.frame(matrix(round(runif(200,1,5)), 10))
# create solution vector
sol <- round(runif(20, 1, 5))

Sekarang terapkan fungsi:

> fscore(d, sol)
 [1] 6 4 2 4 4 3 3 6 2 6

Jika Anda melewatkan argumen data.frame, itu akan mengembalikan data.frame yang dimodifikasi. Saya akan mencoba untuk memperbaiki ini ... Semoga ini bisa membantu!

aL3xa
sumber
6
Satu-liner: rowSums(t(t(d)==sol), na.rm=TRUE). R recycle vector untuk perbandingan. Jika Anda dadalah matriks dengan kasus dalam kolom maka disederhanakan menjadi rowSums(d==sol, na.rm=TRUE).
Marek
0

Saya baru saja mengalami masalah tertentu di mana saya harus menghitung jumlah pernyataan benar dari vektor logis dan ini bekerja paling baik untuk saya ...

length(grep(TRUE, (gene.rep.matrix[i,1:6] > 1))) > 5

Jadi ini mengambil subset dari objek gene.rep.matrix, dan menerapkan tes logis, mengembalikan vektor logis. Vektor ini diletakkan sebagai argumen untuk grep, yang mengembalikan lokasi entri yang BENAR. Panjang kemudian menghitung berapa banyak entri menemukan grep, sehingga memberikan jumlah entri BENAR.

A_Skelton73
sumber
0

Ada juga paket yang disebut bityang dirancang khusus untuk operasi boolean cepat. Ini sangat berguna jika Anda memiliki vektor besar atau perlu melakukan banyak operasi boolean.

z <- sample(c(TRUE, FALSE), 1e8, rep = TRUE)

system.time({
  sum(z) # 0.170s
})

system.time({
  bit::sum.bit(z) # 0.021s, ~10x improvement in speed
})
Daniel Freeman
sumber