Loop files and their contents in R
Following on from a question I posted just a few minutes ago , I need to ask another question. It was not noted in the previous question that I also have to view the contents of each individual file. In other words, I need to iterate over all the files in the directory and through each line of each file.
Each filename looks like this.
airbag.WS-U-E-A.lst
.
delimiter, .lst
extension (read as text).
Each file contains data per line like
/home/nobackup/SONAR/COMPACT/WR-U-E-A/WR-U-E-A0000075.data.ids.xml: <sentence>ja voor den airbag op te pompen eh :p</sentence>
/home/nobackup/SONAR/COMPACT/WR-U-E-A/WR-U-E-A0000129.data.ids.xml: <sentence>Dobby , als ze valt heeft ze dan wel al ne airbag hee</sentence>
What I want to do is, in R, create a new dataset containing data from all files. Ideally it would look like this:
ID | filename | word | component | left-context | right-context
----------------------------------------------------------------------------------------------------------------
1 airbag.WS-U-E-A.lst airbag WS-U-E-A ja voor den op te pompen eh :p
2 airbag.WS-U-E-A.lst airbag WS-U-E-A Dobby , als ze valt heeft ze dan wel al ne hee
ID
is just a string identifier, you can do this:
row.names <- "id"
filename
is the name of the file (obviously) which I can do like this ::
files <- list.files(pattern="*.lst", full.names=T, recursive=FALSE)
d <- data.frame(fileName = unname(sapply(files, basename)))
Then I can remove word
and component
from the filename
d$word <- gsub("\\..+", "", d$fileName, perl=TRUE)
d$component <- gsub("^[^.]+.", "", d$fileName, perl=TRUE)
d$component <- gsub(".lst$", "", d$component, perl=TRUE)
Now comes the tricky part that I haven't figured out yet ...
All the commands I wrote above can only be executed by looping the files and getting their filename. However, as I said, each file contains several sentences that I need to parse and place on different lines. See example above. You will see that the filename, word, and component are identical, but the left and right contexts are not the same. This is because they are two different sentences in the same file.
Perhaps the two file example makes my question clearer.
adapter.WR-P-P-F.lst
/home/nobackup/SONAR/COMPACT/WR-P-P-F/WR-P-P-F0000026.data.ids.xml: <sentence>Een aanpassingseenheid ( adapter ) , aangebracht in een behuizing voornamelijk bestaande uit in- en uitvoereenheden , een koppeleenheid , een geheugeneenheid , een besturingseenheid ( met actieve en passieve elementen en monolitische geïntegreerde schakelingen ) en een elektrische voedingseenheid . ></sentence>
/home/nobackup/SONAR/COMPACT/WR-P-P-F/WR-P-P-F0000026.data.ids.xml: <sentence>ID="1">Het toestel ( adapter ) draagt zorg voor de overbrenging van gegevens , met een snelheid van 10 Mbps ( megabits per seconde ) , tussen meerdere automatische gegevensverwerkende machines in een digitaal netwerk . " ></sentence>
/home/nobackup/SONAR/COMPACT/WR-P-P-F/WR-P-P-F0000034.data.ids.xml: <sentence>Overwegende dat deze sensoren niet zijn ontworpen op de installatie van een gepantserde kabel ; dat de mogelijkheid moet worden geboden dat de gepantserde kabel niet verplicht wordt gesteld voor de aansluiting tussen de sensor en de adapter , maar alleen van de adapter naar het controleapparaat ; dat het bijgevolg noodzakelijk is de verordening dienovereenkomstig te wijzigen ;</sentence>
airbag.WS-U-E-A.lst
/home/nobackup/SONAR/COMPACT/WR-U-E-A/WR-U-E-A0000075.data.ids.xml: <sentence>ja voor den airbag op te pompen eh :p</sentence>
/home/nobackup/SONAR/COMPACT/WR-U-E-A/WR-U-E-A0000129.data.ids.xml: <sentence>Dobby , als ze valt heeft ze dan wel al ne airbag hee</sentence>
If there were only two files in my directory, my R commands did the following:
- View each individual file
- Put each sentence (i.e. each line) on a new line
- Based on which file this sentence is in, fill in the file name, word and component
- With regular expressions, get the left and right context from a sentence
- Assign IDs to each row
The result will look like this:
ID | filename | word | component | left-context | right-context
----------------------------------------------------------------------------------------------------------------
1 adapter.WR-P-P-F.lst adapter WR-P-P-F Een aanpassingseenheid ( ) , aangebracht in een behuizing voornamelijk bestaande uit in- en uitvoere[...]
2 adapter.WR-P-P-F.lst adapter WR-P-P-F ID="1">Het toestel ( ) draagt zorg voor de overbrenging van gegevens [...]
3 adapter.WR-P-P-F.lst adapter WR-P-P-F [...] tussen de sensor en de naar het controleapparaat ; [...]
4 airbag.WS-U-E-A.lst airbag WS-U-E-A ja voor den op te pompen eh :p
5 airbag.WS-U-E-A.lst airbag WS-U-E-A Dobby , als ze valt heeft ze dan wel al ne hee
(I left some content for the sake of brevity, indicated by [...] )
I realize this seems like a pretty big question, however I basically want a way to loop over the files themselves and extract the line in line to a new line, while at the same time entering information about the file itself in separate columns (on the same line). Extracting text from strings is something I would have to do myself. For example, it would take me a long way if I could just get something like this:
ID | filename | word | component | sentence
----------------------------------------------------------------------------------------------------------------
1 adapter.WR-P-P-F.lst adapter WR-P-P-F /home/nobackup/SONAR/COMPACT/WR-P-P-F/WR-P-P-F0000026.data.ids.xml: <sentence>Een aanpassingseenheid ( adapter ) , aangebracht in een behuizing voornamelijk bestaande uit in- en uitvoereenheden , een koppeleenheid , een geheugeneenheid , een besturingseenheid ( met actieve en passieve elementen en monolitische geïntegreerde schakelingen ) en een elektrische voedingseenheid . ></sentence>
2 adapter.WR-P-P-F.lst adapter WR-P-P-F /home/nobackup/SONAR/COMPACT/WR-P-P-F/WR-P-P-F0000026.data.ids.xml: <sentence>ID="1">Het toestel ( adapter ) draagt zorg voor de overbrenging van gegevens , met een snelheid van 10 Mbps ( megabits per seconde ) , tussen meerdere automatische gegevensverwerkende machines in een digitaal netwerk . " ></sentence>
3 adapter.WR-P-P-F.lst adapter WR-P-P-F /home/nobackup/SONAR/COMPACT/WR-P-P-F/WR-P-P-F0000034.data.ids.xml: <sentence>Overwegende dat deze sensoren niet zijn ontworpen op de installatie van een gepantserde kabel ; dat de mogelijkheid moet worden geboden dat de gepantserde kabel niet verplicht wordt gesteld voor de aansluiting tussen de sensor en de adapter , maar alleen van de adapter naar het controleapparaat ; dat het bijgevolg noodzakelijk is de verordening dienovereenkomstig te wijzigen ;</sentence>
4 airbag.WS-U-E-A.lst airbag WS-U-E-A /home/nobackup/SONAR/COMPACT/WR-U-E-A/WR-U-E-A0000075.data.ids.xml: <sentence>ja voor den airbag op te pompen eh :p</sentence>
5 airbag.WS-U-E-A.lst airbag WS-U-E-A /home/nobackup/SONAR/COMPACT/WR-U-E-A/WR-U-E-A0000129.data.ids.xml: <sentence>Dobby , als ze valt heeft ze dan wel al ne airbag hee</sentence>
Hope it's clear what I am trying to say. If you can't ask.
source to share
Create two data.frames and combine them
# what you have so far (file-level data.frame)
files <- list.files(pattern="*.lst", full.names=T, recursive=FALSE)
d <- data.frame(fileName = unname(sapply(files, basename)))
d$word <- gsub("\\..+", "", d$fileName, perl=TRUE)
d$component <- gsub("^[^.]+.", "", d$fileName, perl=TRUE)
d$component <- gsub(".lst$", "", d$component, perl=TRUE)
# new data.frame containing contents of files
e <- do.call(rbind, lapply(files, function(x) {
# create two-column data.frame for each file
## first column is fileName
## second column is data from each file
data.frame(fileName = x, sentence = readLines(x), stringsAsFactors = FALSE)
}))
# the above returns a data.frame `e` containing all file records
# pull out the sentence (probably better ways to do this)
e$sentence <- sapply(strsplit(e$sentence, ".xml: ", fixed = TRUE), `[`, 2)
e$sentence <- gsub("<sentence>", "", e$sentence, fixed = TRUE)
e$sentence <- gsub("</sentence>", "", e$sentence, fixed = TRUE)
# merge `d` and `e` by the common column name `fileName`
out <- merge(d, e, by = "fileName", all = TRUE)
# extract your two "context" variables by splitting the sentence variable by `word`
contexts <- strsplit(out$sentence, out$word)
## this is the left-context (everything to the left of the word)
out$`left-context` <- sapply(contexts, `[`, 1)
## this is the right-context (everything to the right of the word)
out$`right-context` <- sapply(contexts, `[`, 2)
source to share
#set the working directory as shown in (setwd function) and manually move all your files into one directory to keep the analysis nice and clean. You must decide where to store all the files in the directory. Below I showed you that I saved all my files in a "test" directory.
setwd("C:/Users/username/Desktop/test") #windows
setwd("/home/username/Desktop/test") #linux
files <- list.files()
df2 <- data.frame(matrix(nrow = 1, ncol = 5), stringsAsFactors = FALSE)
colnames(df2) <- c("filename", "word", "component", "left_context", "right_context")
for(i in files){
word = sub("([a-z]+)(.)([A-Z-]+)(.*)", "\\1", i)
component = sub("([a-z]+)(.)([A-Z-]+)(.*)", "\\3", i)
list1 <- scan(i, sep = ">", what = list("character", "character"))
context = unlist(lapply(list1[[2]], function(x) gsub('</sentence', '', x) ))
for(j in 1:length(context)){
left_context = strsplit(context[j], word)[[1]][1]
right_context = strsplit(context[j], word)[[1]][2]
df1 <- data.frame(filename = i,
word = word,
component = component,
left_context = left_context,
right_context = right_context,
stringsAsFactors = FALSE)
df2 <- rbind(df2, df1)
}
}
df2 <- df2[2:nrow(df2),]
df2 <- cbind(data.frame(ID = 1:nrow(df2), stringsAsFactors = FALSE), df2)
View(df2)
output:
print(df2)
ID filename word component left_context right_context
1 adapter.WR-P-P-F.lst.txt adapter WR-P-P-F Een aanpassingseenheid ( ) , aangebracht in een behuizing voornamelijk bestaande uit in- en uitvoereenheden , een koppeleenheid , een geheugeneenheid , een besturingseenheid ( met actieve en passieve elementen en monolitische geïntegreerde schakelingen ) en een elektrische voedingseenheid . >
2 adapter.WR-P-P-F.lst.txt adapter WR-P-P-F ID="1">Het toestel ( ) draagt zorg voor de overbrenging van gegevens , met een snelheid van 10 Mbps ( megabits per seconde ) , tussen meerdere automatische gegevensverwerkende machines in een digitaal netwerk . " >
3 adapter.WR-P-P-F.lst.txt adapter WR-P-P-F Overwegende dat deze sensoren niet zijn ontworpen op de installatie van een gepantserde kabel ; dat de mogelijkheid moet worden geboden dat de gepantserde kabel niet verplicht wordt gesteld voor de aansluiting tussen de sensor en de , maar alleen van de
4 airbag.WS-U-E-A.lst.txt airbag WS-U-E-A ja voor den op te pompen eh :p
5 airbag.WS-U-E-A.lst.txt airbag WS-U-E-A Dobby , als ze valt heeft ze dan wel al ne hee
source to share
You can extract data from files using readLines
. In your case, you can use nested loops to create your data - one to iterate over files, and inside that, a loop on each line.
As an example, I installed several files in a directory ~/test
:
$ ls ~/test
file1.txt file2.txt
The files look like this:
$ ls | xargs cat
I am line 1 from file1.
I am line 2 from file1.
I am line 1 from file2.
I am line 2 from file2.
Now I can skip them and grab the filename and its contents. I am not doing any regex or processing this information as you pointed out that you can do it yourself. This is just to illustrate how you can use two loops to add information from the file name and file content on one line:
dir <- '~/test'
df <- data.frame(filename=c(), row.index=c(), row.contents=c())
for (file in list.files(dir)) {
filename = paste0(dir, '/', file)
i <- 1
for (line in readLines(filename)) {
df <- rbind(df, data.frame(filename=filename, row.index=i, row.contents=line))
i <- i + 1
}
}
This leads to the following data. frame:
> df
filename row.index row.contents
1 ~/test/file1.txt 1 I am line 1 from file1.
2 ~/test/file1.txt 2 I am line 2 from file1.
3 ~/test/file2.txt 1 I am line 1 from file2.
4 ~/test/file2.txt 2 I am line 2 from file2.
Using rbind like this is probably not the most efficient way to do this. Another way, which is likely to be more efficient, would be to create a vector for each column in your final dataframe, and then create data from those vectors once you're done. This avoids the potentially expensive rbind () operation.
source to share