Regex: извлечь несколько чисел из одной строки текста - PullRequest
2 голосов
/ 03 июня 2019

проблема

Я скачал серию таблиц с этого сайта:

url <- "https://www.ato.gov.au/Rates/Individual-income-tax-for-prior-years/"
df <- url %>%
  read_html() %>%
  html_table() %>%
  setNames(., url %>%
             read_html() %>%
             html_nodes("caption") %>%
             html_text())

Мне нужно извлечь числа из переменной Tax on this income, содержащейся в таблицах:

$`Resident tax rates for 2016-17`
      Taxable income                         Tax on this income
1        0 – $18,200                                        Nil
2  $18,201 – $37,000               19c for each $1 over $18,200
3  $37,001 – $87,000 $3,572 plus 32.5c for each $1 over $37,000
4 $87,001 – $180,000  $19,822 plus 37c for each $1 over $87,000
5  $180,001 and over $54,232 plus 45c for each $1 over $180,000

В идеале я хотел бы добавить три столбца к каждой таблице со следующими данными:

новый столбец 1: NA, 3572, 19822, 54232

новый столб 2: 19, 32.5, 37, 45

новый столб 3: 18200, 37000, 87000, 180000

Большинство таблиц следуют формату таблицы выше, но в некоторых есть больше строк, а некоторые используют "центы" - то есть строка 2, столбец 2 будет выглядеть так:

19 центов за каждый $ 1 свыше $ 18 200

Таким образом, шаблон регулярного выражения должен соответствовать 19c и 19 центам.

Моя (плохая) попытка

str_extract_all(df$ Ставки резидентского налога на 2016-17 [2], pattern = "(?<=\\$)\\d*,\\d{3}")

Этот шаблон соответствует только суммам в долларах и возвращает вектор символов (оба нежелательны).

Ответы [ 3 ]

2 голосов
/ 03 июня 2019

Здесь используются 3 различных выражения для 3 столбцов

library(dplyr)
library(stringr)

df[[1]] %>%
   mutate(`Tax on this income` = gsub(",", "", `Tax on this income`), 
          col1 = str_extract(`Tax on this income`, "(?<=^\\$)\\d+"), 
          col2 = str_extract(`Tax on this income`, "\\d+.(\\d+)?(?=(\\s+)?c)"),
          col3 = str_extract(`Tax on this income`, "(?<=\\$)\\d+$"))

#      Taxable income                       Tax on this income  col1 col2   col3
#1        0 – $18,200                                      Nil  <NA> <NA>   <NA>
#2  $18,201 – $37,000              19c for each $1 over $18200  <NA>   19  18200
#3  $37,001 – $87,000 $3572 plus 32.5c for each $1 over $37000  3572 32.5  37000
#4 $87,001 – $180,000  $19822 plus 37c for each $1 over $87000 19822   37  87000
#5  $180,001 and over $54232 plus 45c for each $1 over $180000 54232   45 180000

Поскольку "cents" также начинается с "c", это также будет работать, когда у вас вместо "c" вместо "c".

df[[19]] %>%
  mutate(`Tax on this income` = gsub(",", "", `Tax on this income`), 
          col1 = str_extract(`Tax on this income`, "(?<=^\\$)\\d+"), 
          col2 = str_extract(`Tax on this income`, "\\d+.(\\d+)?(?=(\\s+)?c)"),
          col3 = str_extract(`Tax on this income`, "(?<=\\$)\\d+$"))


#     Taxable income                           Tax on this income  col1 col2  col3
#1       $1 – $5,400                                          Nil  <NA> <NA>  <NA>
#2  $5,401 – $20,700              20 cents for each $1 over $5400  <NA>  20   5400
#3 $20,701 – $38,000  $3060 plus 34 cents for each $1 over $20700  3060  34  20700
#4 $38,001 – $50,000  $8942 plus 43 cents for each $1 over $38000  8942  43  38000
#5  $50,001 and over $14102 plus 47 cents for each $1 over $50000 14102  47  50000

Поскольку у вас есть список данных, вы можете использовать map, чтобы применить его к каждому из них

purrr::map(df,.%>%
             mutate(`Tax on this income` = gsub(",", "", `Tax on this income`), 
             col1 = str_extract(`Tax on this income`, "(?<=^\\$)\\d+"), 
             col2 = str_extract(`Tax on this income`, "\\d+.(\\d+)?(?=(\\s+)?c)"),
             col3 = str_extract(`Tax on this income`, "(?<=\\$)\\d+$")))
1 голос
/ 03 июня 2019
pattern = "(?:\\$(\\S+)\\s*plus\\s*)?(\\d++[.]?\\d*)\\s*c.*\\$(\\d++,.*)|.*Nil.*"

clean = function(x){
  nw = gsub(',','',trimws(gsub(pattern,'\\1:\\2:\\3',x[,2],perl=T)))
  cbind(x,read.table(text = nw,fill=T,sep = ':',col.names = paste0('col',1:3)))
}

lapply(df,clean)

`Resident tax rates for 1983-84`
     Taxable income                                Tax on this income     col1 col2  col3
1       $1 – $4,594                                               Nil       NA   NA    NA
2  $4,595 – $19,499                  30 cents for each $1 over $4,595       NA   30  4595
3 $19,500 – $35,787  $4,471.50 plus 46 cents for each $1 over $19,500  4471.50   46 19500
4  $35,788 and over $11,963.98 plus 60 cents for each $1 over $35,788 11963.98   60 35788
0 голосов
/ 03 июня 2019

Это довольно сложно создать выражение.Может быть, давайте спроектируем два выражения для каждой таблицы, а затем запишем остальную часть нашей проблемы.

Например, для таблицы налогооблагаемого дохода мы можем начать с выражения, подобного следующему:

(\d+)(\s+)?(\$?([0-9,]+)[\s–]+\$?([0-9,]+|and over)?)

Демо 1

, а для другихтаблица:

\s+Nil|\$?([0-9,]+)?\s+?(plus\s+)?([0-9,.]+)c?\s+for each\s+(\$1 over)\s+\$?([0-9,]+)

Демонстрация 2

Схема RegEx

jex.im визуализирует регулярные выражения:

enter image description here

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...