<jupyter_start><jupyter_text>More advanced PDF practiceThis example shows how to parse a slightly more difficult PDF using our good friend [`pdfplumber`](https://github.com/jsvine/pdfplumber).For this exercise, we'll pull the data out of a PDF that has a fixed-width table of Colorado county-level voter registration data from April 2008. That file lives here: `../pdfs/apr08_party.pdf`.We'll need to use a text-based strategy and [explicit vertical lines](https://github.com/jsvine/pdfplumbertable-extraction-settings) in the table extraction settings, and we'll make _liberal_ use of [list comprehensions](https://docs.python.org/3/tutorial/datastructures.htmllist-comprehensions), my favorite thing in Python.👉 For more details on using list comprehensions, [see this notebook](../reference/Python%20data%20types%20and%20basic%20syntax.ipynbList-comprehensions).First, let's do our imports:<jupyter_code>import pdfplumber
import pandas as pd<jupyter_output><empty_output><jupyter_text>Now let's open the PDF and dive in.<jupyter_code># open the PDF with the pdfplumber `open` function
with pdfplumber.open('../pdfs/apr08_party.pdf') as pdf:
    
    # the table settings I came up with after fiddling for a bit
    table_settings = {
        'vertical_strategy': 'text', 
        'horizontal_strategy': 'text',
        'explicit_vertical_lines': [95, 205, 245, 290]
    }
    
    # extract the table from the page
    table = pdf.pages[0].extract_table(table_settings=table_settings)
    
    # ~ lots of fiddling at this point to see what the results looked like ~
    
    # use a list comprehension to grab the headers (which are in row 4)
    # and tack on a conditional to remove blank items
    # https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions
    headers = [x for x in table[4] if x]
    
    # remove the useless 'PREC' item from the headers
    headers.remove('PREC')
    
    # create an empty dataframe
    df = pd.DataFrame(columns=headers)
    
    # loop over the table, slicing so that we start with row 6 and leave off some cruft at the bottom
    for row in table[6:-2]:
        
        # clean up the row a little -- remove blanks and kill out commas
        data = [x.replace(',', '') for x in row if x]
        
        # use zip() and dict() to marry the data and headers and then create a dictionary
        # https://docs.python.org/3/library/functions.html#func-dict
        # https://docs.python.org/3/library/functions.html#zip
        d = dict(zip(headers, data))
        
        # append the dict to the dataframe
        df = df.append(d, ignore_index=True)<jupyter_output><empty_output><jupyter_text>Where'd we land?<jupyter_code>df.sort_values('COUNTY NAME')<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.0955321467490011, 'fraction_numerical': 0.014166363966581911, 'mean_word_length': 3.5596026490066226, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 13, 'https://': 6, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '3', 'dir': 'jupyter-structured-clean-dedup', 'id': '464557', 'license': 'MIT', 'n_tokens_mistral': 867, 'n_tokens_neox': 818, 'n_words': 279, 'path': 'completed/_ PDF practice => Colorado voter registration table.ipynb', 'repo_name': 'ireapps/cfj-2018', 'size': '2753'}