<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Demonstration of pdfplumber's table-extraction options
This notebook uses a report from the FBI's National Instant Criminal Background Check System.
Import pdfplumber
Step1:  Load the PDF
Step2:  Get the first page
Step3:  What data would we get if we used the default settings?
We can check by using PageImage.debug_tablefinder()
Step4:  The default settings correctly identify the table's vertical demarcations, but don't capture the horizontal demarcations between each group of five states/territories. So
Step5:  Cleaning up the data
.extract_table worked with our custom settings, but the table it detected contains extraneous headers and footers. Since we know that the Alabama row is the first, and that there are 56 rows we care about (50 states + DC + 4 territories + the "Totals" row), we can slice away the rest
Step6:  The first row
Step7:  The last
Step8:  Now, let's turn those rows into dictionaries, and also convert strings-representing-numbers to the numbers themselves, e.g., "18,870" -&gt; 18870
Step9:  Now here's the first row, parsed
Step10:  Sort the data
For demonstration purposes, let's list the rows with the highest number of handgun-only background checks
Step11:  Use extract_text to extract the report month
It looks like the month of the report is listed in an area 35px to 65px from the top of the page. But there's also some other text directly above and below it. So when we crop for that area, we'll use .within_bbox instead of .crop to select only characters (and other objects) that are fully within the bounding box.
<ASSISTANT_TASK:>
Python Code:
import pdfplumber
print(pdfplumber.__version__)
Explanation: Demonstration of pdfplumber's table-extraction options
This notebook uses a report from the FBI's National Instant Criminal Background Check System.
Import pdfplumber
End of explanation
pdf = pdfplumber.open("../pdfs/background-checks.pdf")
Explanation: Load the PDF
End of explanation
p0 = pdf.pages[0]
im = p0.to_image()
im
Explanation: Get the first page
End of explanation
im.reset().debug_tablefinder()
Explanation: What data would we get if we used the default settings?
We can check by using PageImage.debug_tablefinder():
End of explanation
table_settings = {
    "vertical_strategy": "lines",
    "horizontal_strategy": "text",
    "snap_y_tolerance": 5,
    "intersection_x_tolerance": 15,
}
im.reset().debug_tablefinder(table_settings)
table = p0.extract_table(table_settings)
for row in table[:5]:
    print(row)
Explanation: The default settings correctly identify the table's vertical demarcations, but don't capture the horizontal demarcations between each group of five states/territories. So:
Using custom .extract_table's settings
Because the columns are separated by lines, we use vertical_strategy="lines"
Because the rows are, primarily, separated by gutters between the text, we use horizontal_strategy="text"
To snap together a handful of the gutters at the top which aren't fully flush with one another, we use snap_y_tolerance, which snaps horizontal lines within a certain distance to the same vertical alignment.
And because the left and right-hand extremities of the text aren't quite flush with the vertical lines, we use "intersection_tolerance": 15
End of explanation
core_table = table[4:4+56]
Explanation: Cleaning up the data
.extract_table worked with our custom settings, but the table it detected contains extraneous headers and footers. Since we know that the Alabama row is the first, and that there are 56 rows we care about (50 states + DC + 4 territories + the "Totals" row), we can slice away the rest:
End of explanation
" • ".join(core_table[0])
Explanation: The first row:
End of explanation
" • ".join(core_table[-1])
Explanation: The last:
End of explanation
COLUMNS = [
    "state",
    "permit",
    "handgun",
    "long_gun",
    "other",
    "multiple",
    "admin",
    "prepawn_handgun",
    "prepawn_long_gun",
    "prepawn_other",
    "redemption_handgun",
    "redemption_long_gun",
    "redemption_other",
    "returned_handgun",
    "returned_long_gun",
    "returned_other",
    "rentals_handgun",
    "rentals_long_gun",
    "private_sale_handgun",
    "private_sale_long_gun",
    "private_sale_other",
    "return_to_seller_handgun",
    "return_to_seller_long_gun",
    "return_to_seller_other",
    "totals"
]
def parse_value(i, x):
    if i == 0: return x
    if x == "": return None
    return int(x.replace(",", ""))
from collections import OrderedDict
def parse_row(row):
    return {COLUMNS[i]:parse_value(i, cell)
        for i, cell in enumerate(row)}
data = [ parse_row(row) for row in core_table ]
Explanation: Now, let's turn those rows into dictionaries, and also convert strings-representing-numbers to the numbers themselves, e.g., "18,870" -&gt; 18870:
End of explanation
data[0]
Explanation: Now here's the first row, parsed:
End of explanation
for row in list(reversed(sorted(data, key=lambda x: x["handgun"])))[:6]:
    print("{state}: {handgun:,d} handgun-only checks".format(**row))
Explanation: Sort the data
For demonstration purposes, let's list the rows with the highest number of handgun-only background checks:
End of explanation
month_crop = p0.within_bbox((0, 35, p0.width, 65))
month_crop.to_image()
month_chars = month_crop.extract_text()
month_chars
Explanation: Use extract_text to extract the report month
It looks like the month of the report is listed in an area 35px to 65px from the top of the page. But there's also some other text directly above and below it. So when we crop for that area, we'll use .within_bbox instead of .crop to select only characters (and other objects) that are fully within the bounding box.
End of explanation
<END_TASK>