Python: Working with text files & PubMed references part 2

In the previous post, I explained how I recently had to sort through PubMed references to extract email addresses. As part of the tutorial, I had you download a sample text file containing 14 references, and we read the content of this file in Python. We are now going to continue cleaning the references to have them in a standardized format. In this tutorial we will loop over each line of the imported text and make decisions depending on its content.

Desired format

To make it easier to work with the references, lets make them have a similar format. For this series of tutorials, we will use the following format:

  • [0] = ‘—–‘
  • [1] = journal details
  • [2] = paper title
  • [3] = authors
  • [4] = authors details
  • [5] = abstract
  • [6] = PMID

The above format means that each new reference will start with a seperator ('-----'), followed by 6 other relevant items. If all items are present, the reference will span 7 items in the list containing our cleaned references. For example, if the above items were part of a list called refs_format, we could list all authors of the first reference by typing refs_format[3]. Similarly, refs_format[6] would tell you the PMID of the first reference.

Once cleaned, we will be able to write our references to a new text file where each of these items will be located on a new line. Gone will be empty lines, as well as related text spanning more than one line.

Continuing our reference clean up

I am assuming you have followed the previous tutorial. You should have a list variable called refs in your Python console.

Let’s make a first pass at cleaning our references:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
new_line = '-----'
refs2 = []
for i, line in enumerate(refs):
    if i == 0:
        new_line = line
    else:
        if line.split(' ')[0] == 'PMID:':
            refs2.append(line.split(' ')[0] + ' ' + line.split(' ')[1])
            refs2.append('-----')
        elif line == '\n' and refs[i-1] != '\n' and refs[i-1].split(' ')[0] != 'PMID:':
            refs2.append(new_line)
        elif line == '\n' and refs[i-1] == '\n':
            pass
        elif line != '\n' and refs[i-1] == '\n':
            new_line = line
        else:
            new_line = new_line + ' ' + line

Line 1. Initialize the new_line variable with our seperator.

Line 2. Initialize the refs2 variable as a list. This is where we will store the next iteration of our references.

Line 3. Use a for loop with the enumerate function. We will process each element of the refs variable and use a variable i to keep track of the current loop number for us. The first time through the for loop, i will have a value of 0 (zero) and line will have a value of refs[0]. The second time through the loop, i will have a value of 1 and line will have a value of refs[1]. This will continue until Python has enumerated over all items in refs.

Line 4. Later in the loop, we will be using the current value of line (i.e., refs[i]) as well as the previous value (i.e., refs[i-1]) to do stuff. Indexing the value of refs this way does not apply to the first loop because the index refs[0-1] is equal to refs[-1], which is a Python shorthand for indexing from the end of a list. For example refs [-2] would be the second last item in a list. So we need to force the behaviour of the first iteration through the loop by writing a special if statement.

Line 5. When i is zero, we will assign the current value of line to our new_line variable. In our case, we will assign 1. J Neurosci. 2015 Dec 9;35(49):16159-70. doi: 10.1523/JNEUROSCI.2034-15.2015..

Line 6. When i is not equal to zero, we will execute the following code.

We want to do different things depending on the current and previous value of line. We will control the flow of which of these things is executed using an if elif else structure. Python evaluates each of these logic statements in order. If one of them is True, Python will execute the code indented below the corresponding statement. Once this indented code is completed, Python will skip the rest of the logic tests, go back to the top and assigns to line the next available value, and increments i by one. If none of these statements are True, Python will run the code indented below else.

Line 7. First split the content of line using an empty space as the seperator (i.e., ' '). This is done using split(), which is a method that belongs to string variable types.

As an example, pretend we had a variable x = 'Pubmed is great'. If we applied the split() method to this string variable and specified an empty space as the seperator, x.split(' '), Python would return ['Pubmed', 'is', 'great'], a list containing 3 strings. We could also index items from this list. For example, x.split(' ')[0] would return the first item in the outputed list, which in this case would be 'Pubmed'.

Thus, our code is assessing whether the first word on the current line is equal to PMID: If it is equal to this value, the next two lines of code will be executed; if it is not equal to this value, Python will move on and verify the next logic statement.

Line 8. Given that the logic test found on Line 7 was true, we extract the first and second value of line.split(), concatenate them separated by a space, and append the resulting string to our refs2 variable.

But why not simply append line to refs2? This is because Pubmed references contain additional information on this line that we are not interested in (e.g., [PubMed - in process] ,[PubMed - indexed for MEDLINE]).

Line 9. Because the PMID is the last item of each reference (check your pubmed_results.txt if you want to see for yourself!), we will mark the end of the reference by appending our chosen seperator, '-----'.

Line 10. Verify whether line is an end-of-line character, and whether the previous value of line (i.e., refs2[i -1]) was not (!=) an end-of-line character and not the line starting with PMID:.

Line 11. If all three of the above logic tests evaluated to True, append the current value of new_line to refs2.

Line 12. Verify whether line and the previous value of line are end-of-line characters.

Line 13. In the case of two blank lines following each other, we tell Python to do nothing pass and skip to the top of the for loop.

Line 14. Verify whether line is not an end-of-line character, but the previous value of line was an end-of-line character.

Line 15. In this case, we assign the value of line to our new_line variable. This writes over the previous content of the new_line variable.

Line 16. If all the previous logic tests contained in the if and elif statements evaluated to False, execute the following indented code.

Line 17. Make the current value of new_line equal to the previous value of new_line plus the current value of line; separate these two items with a space. This is the line that will ensure that the text associated with a single category, say the Abstract, is kept together.

Understanding the code

If you are still confused what the above code is doing, a useful learning tool is to add print() statements to the code in order to visualize what is going on. For the current code, you could add the following three lines of code between lines 3 and 4. Below is a revised version of the code that cycles through the content associated with the first reference. For each value of i and line, try figure out which indented lines of code of the if else and the if elif else will be executed.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
new_line = '-----'
refs2 = []
for i, line in enumerate(refs[0:51]):
    print('i = ', i)
    print('line = ',line)
    input('Press Enter to continue...') # used to pause the program
    if i == 0:
        new_line = line
    else:
        if line.split(' ')[0] == 'PMID:':
            refs2.append(line.split(' ')[0] + ' ' + line.split(' ')[1])
            refs2.append('-----')
        elif line == '\n' and refs[i-1] != '\n' and refs[i-1].split(' ')[0] != 'PMID:':
            refs2.append(new_line)
        elif line == '\n' and refs[i-1] == '\n':
            pass
        elif line != '\n' and refs[i-1] == '\n':
            new_line = line
        else:
            new_line = new_line + ' ' + line

What have we done thus far?

Take a look at the contents of your refs2 list by typing print(refs2) in your Python terminal. You will note that related text is now together as part of one item in the list. For example, refs2[2] contains the whole title of the article, whereas in refs this title was split into two list items.

This tutorial has explained in detail how you might change what action is executed based on the value of a string variable (i.e., line). Working with this type of flow control can be daunting at first. If you are having trouble understanding all the steps, consider writing a new, simpler example and manually step through each loop iteration using print() statements. Very soon you will understand how this type of flow control can be used to process text.

In the next tutorial we will apply some of the same principles to further clean up our references.

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s