Как удалить определенные элементы из вектора?

Пример вектора (идентификаторы транскриптов генов):

a <- c('MSTRG.7176.1', 'MSTRG.7176.2', 'AT2G26340.2', 'AT2G26355.1')

Это подмножество длинного вектора, как я могу удалить элемент, начинающийся с «MS», а затем отрезать последние 2 цифры левых элементов?

Связанный пост: Удалить часть строки после «.»

zx8754 15.03.2019 12:25
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
6
1
193
8

Ответы 8

Код

a <- a[!grepl("^MS", a)]
gsub("\\.[0-9]$", "", a)
# [1] "AT2G26340" "AT2G26355"

Объяснение

  1. Используйте regex, чтобы отфильтровать все элементы, начинающиеся с MS.
  2. Используйте regex снова, чтобы заменить точку и последнюю цифру из оставшихся элементов

Если мы хотим полностью избежать регулярных выражений, как упоминает @sindri_baldur, мы можем использовать

string <- a[!startsWith(a, "MS")]
substr(string, 1, nchar(string) - 2)

Или с grep и substr

string <- grep('^MS',a, invert = TRUE, value = TRUE)
substr(string, 1, nchar(string) - 2)
#[1] "AT2G26340" "AT2G26355"

Поскольку у нас есть довольно много новых ответов, мы добавляем тест, включая все из них с вектором длиной 400 КБ.

a <- c('MSTRG.7176.1', 'MSTRG.7176.2', 'AT2G26340.2', 'AT2G26355.1')
a <- rep(a, 100000)

library(microbenchmark)

microbenchmark(
ronak1 = {string <- a[!startsWith(a, "MS")];substr(string, 1, nchar(string) - 2)}, 
ronak2 = {string <- grep('^MS',a, invert = TRUE, value = TRUE);substr(string, 1, nchar(string) - 2)}, 
sotos = {word(a[!str_detect(a, '^MS')], 1, sep = fixed('.'))}, 
thothal = {b1 <- a[!grepl("^MS", a)];gsub("\\.[0-9]$", "", b1)}, 
zx8754 = tools::file_path_sans_ext(a[ !grepl("^MS", a) ]), 
tmfmnk = dirname(chartr(".", "/", a[!grepl("^MS", a)])), 
NelSonGon = {b<-stringi::stri_replace_all(stringi::stri_sub(a,1,-3),regex = "^M.*","");b[grepl('\\w+',b)]}
)


#Unit: milliseconds
#      expr        min         lq       mean     median         uq       max neval
#    ronak1   34.75928   38.58217   45.63393   40.32845   44.24355  225.2581   100
#    ronak2   94.10687   96.72758  110.83819   99.26914  105.98822  938.2969   100
#     sotos 1926.21112 2500.27209 2852.43240 2861.61699 3173.10420 4478.7890   100
#   thothal  155.95328  160.62800  169.02275  164.46494  169.32770  218.5033   100
#    zx8754  172.96970  179.03618  186.12374  183.96887  188.06251  234.1895   100
#    tmfmnk  189.29085  195.14593  208.89245  199.47172  204.40604  547.7497   100
# NelSonGon  186.54426  198.29856  226.19221  206.54542  217.92970  948.2535   100

Ваш второй шаг избегает регулярных выражений, вы можете полностью избежать регулярных выражений с помощью чего-то вроде a[!startsWith(a, "MS")]

sindri_baldur 14.03.2019 09:22

@sindri_baldur верно. Также хорошо иметь опцию без регулярных выражений.

Ronak Shah 14.03.2019 09:33

Я бы переместил ваш второй ответ с startsWith наверх, так как он вдвое быстрее на векторе 100K.

zx8754 14.03.2019 10:02

@zx8754 готово. Это довольно интересно знать. Как вы думаете, использование регулярных выражений делает его медленнее?

Ronak Shah 14.03.2019 10:10

Понятия не имею, на странице справки написано, что это быстрее, чем подстрока и грепл, поэтому решил проверить, и это быстро. Но ест в два раза больше памяти, смотрите бенчмарки ниже.

zx8754 14.03.2019 10:13

Вот еще одна строчка stringr,

library(stringr)

word(a[!str_detect(a, '^MS')], 1, sep = fixed('.'))
#[1] "AT2G26340" "AT2G26355"

Думайте о них как об именах файлов и отбрасывайте расширение:

tools::file_path_sans_ext(a[ !grepl("^MS", a) ])
# [1] "AT2G26340" "AT2G26355"

Поскольку у человека около 200 тысяч транскриптов, вот эталон:

a <- c('MSTRG.7176.1', 'MSTRG.7176.2', 'AT2G26340.2', 'AT2G26355.1')
a <- rep(a, 25000)

library(stringr)

bench::mark(
  x1 = {
    string <- grep('^MS',a, invert = TRUE, value = TRUE)
    substr(string, 1, nchar(string) - 2) },
  x2 = {
    string <- a[!startsWith(a, "MS")]
    substr(string, 1, nchar(string) - 2)},
  x3 = {
    word(a[!str_detect(a, '^MS')], 1, sep = fixed('.'))  
  },
  x4 = {
    gsub("\\.[0-9]$", "", a[ !grepl("^MS", a) ])},
  x5 = {
    tools::file_path_sans_ext(a[ !grepl("^MS", a) ])  
  }
  )

# A tibble: 5 x 14
# expression      min     mean  median     max `itr/sec` mem_alloc  n_gc n_itr total_time result memory  time  gc   
# <chr>      <bch:tm> <bch:tm> <bch:t> <bch:t>     <dbl> <bch:byt> <dbl> <int>   <bch:tm> <list> <list>  <lis> <lis>
# x1           20.3ms   21.3ms    21ms  28.1ms     46.9     1.91MB     1    24      512ms <chr ~ <Rprof~ <bch~ <tib~
# x2           11.7ms   12.6ms  12.3ms  17.8ms     79.3     2.86MB     3    40      505ms <chr ~ <Rprof~ <bch~ <tib~
# x3          668.5ms  668.5ms 668.5ms 668.5ms      1.50   10.54MB     9     1      668ms <chr ~ <Rprof~ <bch~ <tib~
# x4           23.8ms   24.6ms  24.1ms  32.2ms     40.7      2.1MB     1    21      516ms <chr ~ <Rprof~ <bch~ <tib~
# x5           33.8ms   35.2ms  34.7ms  40.9ms     28.4      2.1MB     1    15      528ms <chr ~ <Rprof~ <bch~ <tib~

Вы также можете попробовать:

dirname(chartr(".", "/", a[!grepl("^MS", a)]))

[1] "AT2G26340" "AT2G26355"

Во-первых, с помощью grepl() он определяет падежи, начинающиеся с MS. Во-вторых, он заменяет . на /, используя chartr(). Наконец, dirname() возвращает часть строк до последней /.

Учитывая, что могут быть элементы, не начинающиеся с MS, но содержащие два или более десятичных знака, вы можете использовать:

chartr("/", ".", dirname(chartr(".", "/", a[!grepl("^MS", a)])))

Это то же самое, что и первая возможность, но заменяет оставшиеся / обратно на ..

Или второй вариант с заменой chartr() на gsub():

gsub("/", ".", dirname(gsub(".", "/", a[!grepl("^MS", a)], fixed = TRUE)), 
     fixed = TRUE)

Предоставление stringi возможности: я предпочитаю один лайнер, но, возможно, может быть достаточно двухстрочного решения.

 b<-stringi::stri_replace_all(stringi::stri_sub(a,1,-3),regex = "^M.*","")
b[grepl('\\w+',b)]
#[1] "AT2G26340" "AT2G26355"

Я не вижу сочетания sub() и startsWith(), поэтому

sub(".{2}$", "", a[!startsWith(a, "MS")])
# [1] "AT2G26340" "AT2G26355"

Другие вопросы по теме