Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Read PDFs

harvest() reads one PDF, or every PDF in a folder, and returns clean sentences and a drop log.

from theoryminer import harvest

data = harvest("sample_files")                       # every PDF in the folder
data = harvest("book.pdf", pages="12-540")           # one PDF, some pages only

The result has two lists:

KeyWhat it holds
data["sentences"]one dict per kept sentence
data["dropped"]one dict per dropped row, with its drop_reason

What a sentence holds

KeyMeaning
doc_idthe file name without .pdf
pagethe page number, as in a PDF viewer
sent_ida unique id, for example d000p0012s003: document 0, paragraph 12, sentence 3
sectionthe last heading before the sentence
rawthe sentence as the sentence splitter gave it
cleanthe sentence after the later steps; causenet() reads this text
flag"", or a flag for a doubtful sentence (see below)
doi, titlethe DOI and the title of the paper, or ""

The cleaning steps

The steps run in this order. You can change the list with steps=.

StepWhat it does
fix_unicodereplaces curly quotes, long dashes, odd spaces, and removes invisible marks
strip_markdownremoves Markdown links and emphasis that the PDF extractor adds
drop_running_headsdrops short lines that repeat 3 or more times, such as a chapter header
drop_junk_linesdrops structural lines: headings, table rows, page numbers, the reference section
join_paragraphsjoins the lines of a paragraph, and a word that a hyphen cut. It joins a paragraph that a page break cut when the first part has no final punctuation and the next part starts in lowercase.
split_sentencessplits the paragraphs into sentences
strip_citationsremoves citations such as “(Ryan & Deci, 2000)” from clean
strip_statsremoves statistics such as “(p < .05)” from clean
drop_junk_sentencesdrops sentences that are not research text
filter_lengthdrops sentences that are too short or too long

To switch off one step, give the list without it:

from theoryminer import DEFAULT_STEPS

steps = [step for step in DEFAULT_STEPS if step != "strip_stats"]
data = harvest("sample_files", steps=steps)

You can also add a function of your own. It takes the list of row dicts and returns it. The tutorial notebook, section 1.3b, shows how.

Why a row is dropped

harvest() never deletes a row. A step marks it as dropped and writes the reason.

ReasonThe row is
running_heada line of 90 characters or fewer that repeats 3 or more times
reference_sectiona line inside a references, bibliography, notes or acknowledgements section
headinga heading; its text becomes the section of the next sentences
table_rowa row of a table
page_numbera line that holds only a page number
chapter_markera line that holds only “CHAPTER” or “PART”
front_back_mattera line that holds only “Index”, “Contents” or “Appendix”
doi_urla line that starts with a DOI or a web address
ref_fragmentone word with a digit and no space, a piece of a reference
low_alphaa line or a sentence with too few letters
licence_textthe licence or copyright text of a journal
caption_or_keywordsa text that starts with “Keywords:”, “Source:”, “Note:” or “Abstract:”, or a caption such as “Figure 1” or “Table 2”
index_entryan entry of a book index
spaced_letterstext with spaces between its letters, such as “R e s u l t s”
no_lowercasea sentence without a lowercase letter
digit_heavya sentence with too many digits
too_shorta sentence with fewer than min_words words (default 4)
too_longa sentence with more than max_words words (default 80)
empty_pagea page without text
not_pdf, unreadable_pdfa file in the folder that is not a PDF, or that cannot be read

Flags

A flag marks a doubtful sentence. The sentence stays in data["sentences"].

FlagMeaning
front_matterthe PDF extractor mixed journal sidebar text into the sentence: an editor name, a date, an e-mail address
fragmentthe sentence has no final punctuation
longthe sentence has more than 80 words. With the default max_words=80, these sentences are dropped as too_long, so the flag shows only when you raise max_words.

To keep the flagged sentences away from the model:

sentences = [s for s in data["sentences"] if not s["flag"]]

Pages

Page 1 is the first page, as in a PDF viewer.

harvest("book.pdf", pages=12)               # one page
harvest("book.pdf", pages="12-540")         # a range
harvest("book.pdf", pages="3,7,9-12")       # a list of pages and ranges
harvest("book.pdf", skip_pages=[4, 5])      # every page except 4 and 5

For a folder, the same pages apply to every PDF.

The page cache

Text extraction is the slow part. harvest() writes the text of each PDF to <pdf>.pages.json beside the PDF. The next run reads that file. Use cache=False to read every PDF again and write no file.

A bad file

For one PDF, a missing or broken file raises a ValueError. For a folder, a bad file does not stop the run. The drop log gets one row for the file, with page 0 and the reason not_pdf or unreadable_pdf. harvest() reads the folder only, not its subfolders.

See the full list of settings in the reference of harvest().