Correcting strings scattered over multiple lines b

2020-02-16 05:12发布

问题:

I uploaded a .txt file in to R as follows: Election_Parties <- readr::read_lines("Election_Parties.txt") The following text is in the file: pastebin link.

The text more or less looks as follows (Please use actual file for solution!):

BOLIVIA
P1-Nationalist Revolutionary Movement-Free Bolivia Movement (Movimiento 
Nacionalista Revolucionario [MNR])
P19-Liberty and Justice (Libertad y Justicia [LJ])
P20-Tupak Katari Revolutionary Movement (Movimiento Revolucionario Tupak Katari [MRTK])

COLOMBIA
P1-Democratic Aliance M-19 (Alianza Democratica M-19 [AD-M19])
P2-National Popular Alliance (Alianza Nacional Popular [ANAPO])
P3-Indigenous Authorities of Colombia (Autoridades Indígenas 
de Colombia)

I would like to have all information about a party on one line, no matter how long it is.

DESIRED OUTPUT:

BOLIVIA
P1-Nationalist Revolutionary Movement-Free Bolivia Movement (Movimiento Nacionalista Revolucionario 
P19-Liberty and Justice (Libertad y Justicia [LJ])
P20-Tupak Katari Revolutionary Movement (Movimiento Revolucionario Tupak Katari [MRTK])

COLOMBIA
P1-Democratic Aliance M-19 (Alianza Democratica M-19 [AD-M19])
P2-National Popular Alliance (Alianza Nacional Popular [ANAPO])
P3-Indigenous Authorities of Colombia (Autoridades Indígenas de Colombia)

The following answer: strsplit(paste(Election_Parties, collapse=" "), "\\s+(?=P\\d+-)", perl=TRUE)[[1]] from this LINK, works to correct the strings, but it does not deal with the headers (BOLIVIA, COLUMBIA & the empty lines) properly. Dealing with this is important because I want to apply this solution afterwards.

Although I got an answer in the commentsof that post which worked on the example, it does not work on my text file.

How can I adapt the solution to deal with (leave alone) the headers and empty lines?

回答1:

I turned the whole thing into a tidy and useful format. Have a look:

First I read in the file:

lines <- readr::read_lines("https://pastebin.com/raw/jSrvTa7G")
head(lines)
#> [1] ""                                                                                                        
#> [2] "ALBANIA"                                                                                                 
#> [3] "P1-Democratic Alliance Party (Partia Aleanca Democratike [AD])"                                          
#> [4] "P2-National Unity Party (Partia Uniteti Kombëtar [PUK])"                                                 
#> [5] "P3-Social Spectrum Parties-Party of National Unity (Partitë e Spektrit Social-Partia e Unitetit Kombëtar"
#> [6] "[PSHS-PUK])"

I split the raw format into entries by looking for empty lines, which occur just before a new entry:

entries <- split(lines, cumsum(grepl("^$|^ $", lines)))

Then I loop through every entry and turn it into a tibble:

library(stringr)
library(dplyr)
df <- lapply(entries, function(entry) {
  entry <- entry[!grepl("^$|^ $", entry)] # remove empty elements
  header <- entry[1] # first non empty is the header
  entry <- tail(entry, -1)  # remove header from entry
  desc <- str_extract(entry, "^P\\d+-")  # extract description

  for (l in which(is.na(desc))) { # collapse lines that go over 2 elements
    entry[l - 1] <- paste(entry[l - 1], entry[l], sep = " ")
  }

  entry <- entry[!is.na(desc)]
  desc <- desc[!is.na(desc)]

  # turn into nice format
  df <- tibble::tibble(
    header,
    desc,
    entry
  )
  df$entry <- str_replace_all(df$entry, fixed(df$desc), "") # remove description from entry
  return(df)
}) %>% 
  bind_rows() # turn list into one data.frame

And now we have a really nice data.frame we can easily work with:

df
#> # A tibble: 5,525 x 3
#>    header  desc  entry                                                     
#>    <chr>   <chr> <chr>                                                     
#>  1 ALBANIA P1-   Democratic Alliance Party (Partia Aleanca Democratike [AD~
#>  2 ALBANIA P2-   National Unity Party (Partia Uniteti Kombëtar [PUK])      
#>  3 ALBANIA P3-   Social Spectrum Parties-Party of National Unity (Partitë ~
#>  4 ALBANIA P4-   Alliance Party for Solidarity and Welfare (Partia Aleanca~
#>  5 ALBANIA P5-   Albanian Democratic Union-Alliance for Freedom, Justice a~
#>  6 ALBANIA P6-   Liberal Democrat Party (Partia Bashkimi Liberal Demokrat ~
#>  7 ALBANIA P7-   Linking Blerta Albanian Party (Partia Lidhja e Blertë Shq~
#>  8 ALBANIA P8-   Democratic Movement for Integration (Lëvizja Demokratike ~
#>  9 ALBANIA P9-   Movement of Human Rights and Freedoms Party (Partia Lëviz~
#> 10 ALBANIA P10-  Socialist Party of Albania (Partia Socialiste e Shqipëris~
#> # ... with 5,515 more rows

The strings which are scattered over multiple lines are corrected in this bit:

  for (l in which(is.na(desc))) { # collapse lines that go over 2 elements
    entry[l - 1] <- paste(entry[l - 1], entry[l], sep = " ")
  }

desc will be NA in cases where the line does not begin with e.g., "P1-" (1 can be any number). If this is the case the line is collapse with the previous entry. Later on NA are removed leaving the information only in the correct line.