--- title: "contentanalysis" subtitle: "Comprehensive Analysis of Scientific Papers with Bibliometric Enrichment" author: "By Massimo Aria" date: "`r Sys.Date()`" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Introduction to contentanalysis} %\VignetteEncoding{UTF-8} %\VignetteEngine{knitr::rmarkdown} editor_options: markdown: wrap: 72 --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", eval = TRUE # Set to TRUE if you want to run the examples ) ``` ```{r setup} library(contentanalysis) library(dplyr) ``` ## Introduction The `contentanalysis` package is a comprehensive R toolkit designed for in-depth analysis of scientific literature. It bridges the gap between raw PDF documents and structured, analyzable data by combining advanced text extraction, citation analysis, and bibliometric enrichment from external databases. **AI-Enhanced PDF Import**: The package supports AI-assisted PDF text extraction through Google's Gemini API, enabling more accurate parsing of complex document layouts. To use this feature, you need to obtain an API key from [Google AI Studio](https://aistudio.google.com/apikey). **Integration with bibliometrix**: This package complements the science mapping analyses available in `bibliometrix` and its Shiny interface `biblioshiny`. If you want to perform content analysis within a user-friendly Shiny application with all the advantages of an interactive interface, simply install `bibliometrix` and launch `biblioshiny`, where you'll find a dedicated **Content Analysis** menu that implements all the analyses and outputs of this library. ### What Makes It Unique? The package goes beyond simple PDF parsing by creating a multi-layered analytical framework: 1. **Intelligent PDF Processing**: Extracts text from multi-column PDFs while preserving document structure (sections, paragraphs, references) 2. **Citation Intelligence**: Detects and extracts citations in multiple formats (numbered, author-year, narrative, parenthetical) and maps them to their precise locations in the document 3. **Bibliometric Enrichment**: Automatically retrieves and integrates metadata from external sources: - **CrossRef API**: Retrieves structured reference data including authors, publication years, journals, and DOIs - **OpenAlex**: Enriches references with additional metadata, filling gaps and providing comprehensive bibliographic information 4. **Citation-Reference Linking**: Implements sophisticated matching algorithms to connect in-text citations with their corresponding references, handling various citation styles and ambiguous cases 5. **Context-Aware Analysis**: Extracts the textual context surrounding each citation, enabling semantic analysis of how references are used throughout the document 6. **Network Visualization**: Creates interactive networks showing citation co-occurrence patterns and conceptual relationships within the document ### The Complete Workflow ``` PDF Document → Text Extraction → Citation Detection → Reference Parsing ↓ CrossRef/OpenAlex APIs ↓ Citation-Reference Matching → Enriched Dataset ↓ Network Analysis + Text Analytics + Bibliometric Indicators ``` This vignette demonstrates the main features using a real open-access scientific paper. ## Getting Started ### Download Example Paper We'll use an open-access paper on Machine Learning with Applications: Aria, M., Cuccurullo, C., & Gnasso, A. (2021). **A comparison among interpretative proposals for Random Forests**. *Machine Learning with Applications*, 6, 100094. The paper is available at: https://doi.org/10.1016/j.mlwa.2021.100094. **Abstract**: The growing success of Machine Learning (ML) is making significant improvements to predictive models, facilitating their integration in various application fields. Despite its growing success, there are some limitations and disadvantages: the most significant is the lack of interpretability that does not allow users to understand how particular decisions are made. Our study focus on one of the best performing and most used models in the Machine Learning framework, the Random Forest model. It is known as an efficient model of ensemble learning, as it ensures high predictive precision, flexibility, and immediacy; it is recognized as an intuitive and understandable approach to the construction process, but it is also considered a Black Box model due to the large number of deep decision trees produced within it. The aim of this research is twofold. We present a survey about interpretative proposal for Random Forest and then we perform a machine learning experiment providing a comparison between two methodologies, inTrees, and NodeHarvest, that represent the main approaches in the rule extraction framework. The proposed experiment compares methods performance on six real datasets covering different data characteristics: n. of observations, balanced/unbalanced response, the presence of categorical and numerical predictors. This study contributes to picture a review of the methods and tools proposed for ensemble tree interpretation, and identify, in the class of rule extraction approaches, the best proposal. ```{r download} # Download example paper paper_url <- "https://raw.githubusercontent.com/massimoaria/contentanalysis/master/inst/examples/example_paper.pdf" download.file(paper_url, destfile = "example_paper.pdf", mode = "wb") ``` ## PDF Import and Section Detection ### Basic Import ```{r import-basic} # Import with automatic section detection doc <- pdf2txt_auto("example_paper.pdf", n_columns = 2, citation_type = "author_year") # Check what sections were detected names(doc) ``` The function automatically detects common academic sections like Abstract, Introduction, Methods, Results, Discussion, etc. ### Manual Column Specification For papers with specific layouts: ```{r import-manual, eval=FALSE} # Single column doc_single <- pdf2txt_auto("example_paper.pdf", n_columns = 1) # Three columns doc_three <- pdf2txt_auto("example_paper.pdf", n_columns = 3) # Without section splitting text_only <- pdf2txt_auto("example_paper.pdf", sections = FALSE) ``` ## Comprehensive Content Analysis with API Enrichment ### Full Analysis with CrossRef and OpenAlex Integration The `analyze_scientific_content()` function performs a comprehensive analysis in a single call, automatically enriching the data with external metadata: ```{r analysis} analysis <- analyze_scientific_content( text = doc, doi = "10.1016/j.mlwa.2021.100094", # Paper's DOI for CrossRef lookup mailto = "your@email.com", # Required for CrossRef API citation_type = "author_year", # Citation style window_size = 10, # Words around citations remove_stopwords = TRUE, ngram_range = c(1, 3), use_sections_for_citations = TRUE ) ``` **What happens behind the scenes:** 1. Extracts all citations from the document text 2. Retrieves reference metadata from **CrossRef** using the paper's DOI 3. Enriches references with additional data from **OpenAlex** (citation counts, open access status, complete author lists) 4. Matches in-text citations to references with confidence scoring 5. Performs text analysis and computes bibliometric indicators 6. Extracts citation contexts and analyzes co-occurrence patterns ### Understanding the Results The analysis object contains multiple components: ```{r results-structure} names(analysis) ``` **Key components:** - `text_analytics`: Basic statistics and word frequencies - `citations`: All extracted citations with metadata - `citation_contexts`: Citations with surrounding text - `citation_metrics`: Citation type distribution, density - `citation_references_mapping`: Matched citations to references - `parsed_references`: Structured reference list (enriched with API data) - `references_oa`: OpenAlex metadata for references - `word_frequencies`: Word frequency table - `ngrams`: N-gram frequency tables - `network_data`: Citation co-occurrence data ### Summary Statistics ```{r summary} analysis$summary ``` Key metrics include: - Total words analyzed - Number of citations extracted (by type) - Number of references parsed from CrossRef/OpenAlex - Citations successfully matched to references - Match quality distribution - Lexical diversity - Citation density per 1000 words ## Working with Enriched Reference Data ### Exploring Reference Sources ```{r reference-sources} # View enriched references head(analysis$parsed_references[, c("ref_first_author", "ref_year", "ref_journal", "ref_source")]) # Check data sources table(analysis$parsed_references$ref_source) ``` The `ref_source` column indicates where the data originated: - `"crossref"`: Retrieved from CrossRef API - `"parsed"`: Extracted from document's reference section - References may be further enriched with OpenAlex data ### Accessing OpenAlex Metadata If OpenAlex data was successfully retrieved, you can access additional metrics: ```{r openalex-data} # Check if OpenAlex data is available if (!is.null(analysis$references_oa)) { # View enriched metadata head(analysis$references_oa[, c("title", "publication_year", "cited_by_count", "type", "is_oa")]) # Analyze citation impact cat("Citation impact statistics:\n") print(summary(analysis$references_oa$cited_by_count)) # Open access status if ("is_oa" %in% names(analysis$references_oa)) { oa_count <- sum(analysis$references_oa$is_oa, na.rm = TRUE) cat("\nOpen Access references:", oa_count, "out of", nrow(analysis$references_oa), "\n") } } ``` ### Citation-Reference Matching Quality ```{r matching-quality} # View matching results with confidence levels matched <- analysis$citation_references_mapping %>% select(citation_text_clean, cite_author, cite_year, ref_authors, ref_year, match_confidence) head(matched) # Match quality distribution cat("Match quality distribution:\n") print(table(matched$match_confidence)) # High-confidence matches high_conf <- matched %>% filter(match_confidence %in% c("high", "high_second_author")) cat("\nHigh-confidence matches:", nrow(high_conf), "out of", nrow(matched), "\n") ``` ## Citation Analysis ### Citation Extraction The package detects multiple citation formats: ```{r citations} # View all citations head(analysis$citations) # Citation types found table(analysis$citations$citation_type) # Citations by section analysis$citation_metrics$section_distribution ``` ### Citation Type Analysis ```{r citation-types} # Narrative vs. parenthetical analysis$citation_metrics$narrative_ratio # Citation density cat("Citation density:", analysis$citation_metrics$density$citations_per_1000_words, "citations per 1000 words\n") ``` ### Citation Contexts Extract the text surrounding each citation: ```{r contexts} # View citation contexts with matched references contexts <- analysis$citation_contexts %>% select(citation_text_clean, section, ref_full_text, full_context, match_confidence) head(contexts) # Find citations in specific section intro_citations <- analysis$citation_contexts %>% filter(section == "Introduction") cat("Citations in Introduction:", nrow(intro_citations), "\n") ``` ## Citation Network Visualization The package creates interactive network visualizations showing how citations co-occur within your document. Citations that appear close together are connected, revealing citation patterns and relationships. ### Creating the Network ```{r network-create, fig.width=8, fig.height=6} # Create interactive citation network network <- create_citation_network( citation_analysis_results = analysis, max_distance = 800, # Maximum distance in characters min_connections = 2, # Minimum connections to include a node show_labels = TRUE # Show citation labels ) # Display the network network ``` ### Understanding Network Features The network visualization includes several visual elements: - **Node size**: Larger nodes have more connections - **Node color**: Indicates the primary section where the citation appears - **Node border**: Thicker borders (3px) indicate citations appearing in multiple sections - **Edge thickness**: Thicker edges connect citations that appear closer together - **Edge color**: - Red: Very close citations (≤300 characters) - Blue: Moderate distance (≤600 characters) - Gray: Distant citations (>600 characters) - **Interactive features**: Zoom, pan, drag nodes, highlight neighbors on hover ### Network Statistics Access detailed statistics about the network: ```{r network-stats} # Get network statistics stats <- attr(network, "stats") # Network size cat("Number of nodes:", stats$n_nodes, "\n") cat("Number of edges:", stats$n_edges, "\n") cat("Average distance:", stats$avg_distance, "characters\n") cat("Maximum distance:", stats$max_distance, "characters\n") # Distribution by section print(stats$section_distribution) # Citations appearing in multiple sections if (nrow(stats$multi_section_citations) > 0) { cat("\nCitations appearing in multiple sections:\n") print(stats$multi_section_citations) } # Color mapping cat("\nSection colors:\n") print(stats$section_colors) ``` ### Customizing the Network You can customize the network based on your analysis needs: ```{r network-custom, eval=FALSE} # Focus on very close citations only network_close <- create_citation_network( analysis, max_distance = 300, min_connections = 1 ) # Show only highly connected "hub" citations network_hubs <- create_citation_network( analysis, max_distance = 1000, min_connections = 5, show_labels = TRUE ) # Clean visualization without labels network_clean <- create_citation_network( analysis, max_distance = 800, min_connections = 2, show_labels = FALSE ) ``` ### Interpreting the Network The citation network can reveal: 1. **Citation clusters**: Groups of related citations that frequently appear together 2. **Hub citations**: Highly connected citations that appear throughout the document 3. **Section patterns**: How citations are distributed across different sections 4. **Co-citation patterns**: Which references are cited together ```{r network-analysis} # Find hub citations (most connected) hub_threshold <- quantile(stats$section_distribution$n, 0.75) cat("Hub citations (top 25%):\n") print(stats$section_distribution %>% filter(n >= hub_threshold)) # Analyze network density network_density <- stats$n_edges / (stats$n_nodes * (stats$n_nodes - 1) / 2) cat("\nNetwork density:", round(network_density, 3), "\n") ``` ### Citation Co-occurrence Data You can also access the raw co-occurrence data: ```{r network-data} # View raw co-occurrence data network_data <- analysis$network_data head(network_data) # Citations appearing very close together close_citations <- network_data %>% filter(distance < 100) # Within 100 characters cat("Number of very close citation pairs:", nrow(close_citations), "\n") ``` ## Text Analysis ### Word Frequencies ```{r word-freq} # Top 20 most frequent words head(analysis$word_frequencies, 20) ``` ### N-gram Analysis ```{r ngrams} # Bigrams head(analysis$ngrams$`2gram`) # Trigrams head(analysis$ngrams$`3gram`) ``` ### Readability Metrics Calculate readability indices for the document: ```{r readability} # Calculate readability for the full text readability <- calculate_readability_indices( doc$Full_text, detailed = TRUE ) print(readability) # Compare readability across sections sections_to_analyze <- c("Abstract", "Introduction", "Methods", "Discussion") readability_by_section <- lapply(sections_to_analyze, function(section) { if (section %in% names(doc)) { calculate_readability_indices(doc[[section]], detailed = FALSE) } }) names(readability_by_section) <- sections_to_analyze # View results do.call(rbind, readability_by_section) ``` ## Word Distribution Analysis Track how specific terms are distributed across the document: ```{r word-dist} # Terms of interest terms <- c("random forest", "machine learning", "accuracy", "tree") # Calculate distribution dist <- calculate_word_distribution( text = doc, selected_words = terms, use_sections = TRUE ) # View results dist %>% select(segment_name, word, count, percentage) %>% arrange(segment_name, desc(percentage)) ``` ### Visualization ```{r plot, fig.width=8, fig.height=5, eval=TRUE} # Interactive plot plot_word_distribution( dist, plot_type = "line", show_points = TRUE, smooth = TRUE ) # Area plot plot_word_distribution( dist, plot_type = "area" ) ``` ## Advanced Examples ### Finding Specific Citations ```{r find-citations} # Citations to specific author analysis$citation_references_mapping %>% filter(grepl("Breiman", ref_authors, ignore.case = TRUE)) # Citations in Discussion section analysis$citations %>% filter(section == "Discussion") %>% select(citation_text, citation_type, section) ``` ### Analyzing Highly Cited References If OpenAlex data is available, analyze citation impact: ```{r citation-impact} if (!is.null(analysis$references_oa)) { # Top cited references top_cited <- analysis$references_oa %>% arrange(desc(cited_by_count)) %>% select(title, publication_year, cited_by_count, is_oa) %>% head(10) print(top_cited) } ``` ### Custom Stopwords ```{r custom-stop, eval=FALSE} custom_stops <- c("however", "therefore", "thus", "moreover") analysis_custom <- analyze_scientific_content( text = doc, doi = "10.1016/j.mlwa.2021.100094", mailto = "your@email.com", custom_stopwords = custom_stops, remove_stopwords = TRUE ) ``` ### Segment-based Analysis For documents without clear sections: ```{r segments, fig.height=5, fig.width=8, eval=FALSE} # Divide into 20 equal segments dist_segments <- calculate_word_distribution( text = doc, selected_words = terms, use_sections = FALSE, n_segments = 20 ) plot_word_distribution(dist_segments, smooth = TRUE) ``` ## Setting Up External API Access ### CrossRef API CrossRef provides structured bibliographic data: ```{r crossref-setup, eval=FALSE} # Always provide your email for the polite pool analysis <- analyze_scientific_content( text = doc, doi = "10.xxxx/xxxxx", mailto = "your@email.com" # Required for CrossRef polite pool ) ``` **CrossRef Features:** - Retrieves authors, publication year, journal/source, article title, DOI - Polite pool requires email (higher rate limits) - More info: https://api.crossref.org ### OpenAlex API OpenAlex provides comprehensive scholarly metadata: ```{r openalex-setup, eval=FALSE} # Optional: Set API key for higher rate limits # Get free key at: https://openalex.org/ openalexR::oa_apikey("your-api-key-here") # Then run your analysis as usual analysis <- analyze_scientific_content( text = doc, doi = "10.xxxx/xxxxx", mailto = "your@email.com" ) ``` **OpenAlex Features:** - Complete author lists, citation counts, open access status - Institutional affiliations, funding information - 100,000 requests/day (polite pool with email) - 10 requests/second rate limit - More info: https://openalex.org ## Export Results ### Save to CSV ```{r export, eval=FALSE} # Export citations write.csv(analysis$citations, "citations.csv", row.names = FALSE) # Export matched references with confidence scores write.csv(analysis$citation_references_mapping, "matched_citations.csv", row.names = FALSE) # Export enriched references write.csv(analysis$parsed_references, "enriched_references.csv", row.names = FALSE) # Export OpenAlex metadata (if available) if (!is.null(analysis$references_oa)) { write.csv(analysis$references_oa, "openalex_metadata.csv", row.names = FALSE) } # Export word frequencies write.csv(analysis$word_frequencies, "word_frequencies.csv", row.names = FALSE) # Export network statistics if (!is.null(network)) { stats <- attr(network, "stats") write.csv(stats$section_distribution, "network_section_distribution.csv", row.names = FALSE) if (nrow(stats$multi_section_citations) > 0) { write.csv(stats$multi_section_citations, "network_multi_section_citations.csv", row.names = FALSE) } } ``` ## Workflow for Multiple Papers ```{r batch, eval=FALSE} # Process multiple papers with API enrichment papers <- c("paper1.pdf", "paper2.pdf", "paper3.pdf") dois <- c("10.xxxx/1", "10.xxxx/2", "10.xxxx/3") results <- list() networks <- list() for (i in seq_along(papers)) { # Import PDF doc <- pdf2txt_auto(papers[i], n_columns = 2) # Analyze with API enrichment results[[i]] <- analyze_scientific_content( doc, doi = dois[i], mailto = "your@email.com" ) # Create network for each paper networks[[i]] <- create_citation_network( results[[i]], max_distance = 800, min_connections = 2 ) } # Combine citation counts citation_counts <- sapply(results, function(x) x$summary$citations_extracted) names(citation_counts) <- papers # Compare network statistics network_stats <- lapply(networks, function(net) { stats <- attr(net, "stats") c(nodes = stats$n_nodes, edges = stats$n_edges, avg_distance = stats$avg_distance) }) do.call(rbind, network_stats) # Analyze reference sources across papers ref_sources <- lapply(results, function(x) { if (!is.null(x$parsed_references)) { table(x$parsed_references$ref_source) } }) names(ref_sources) <- papers ref_sources ``` ## Conclusion The `contentanalysis` package provides a complete toolkit for analyzing scientific papers with bibliometric enrichment: 1. **Import**: Handle multi-column PDFs with structure preservation 2. **Extract**: Detect citations in multiple formats 3. **Enrich**: Retrieve metadata from CrossRef and OpenAlex APIs 4. **Match**: Link citations to references automatically with confidence scoring 5. **Analyze**: Word frequencies, n-grams, citation contexts, readability 6. **Visualize**: Interactive plots of word distributions and citation networks 7. **Network**: Explore citation co-occurrence patterns ### Key Advantages - **Automated enrichment**: No manual reference entry needed - **Multiple data sources**: Combines CrossRef and OpenAlex for complete coverage - **Intelligent matching**: Handles various citation styles and ambiguous cases - **Context-aware**: Extracts and analyzes citation contexts - **Interactive visualization**: Dynamic networks and plots - **Comprehensive output**: Structured data ready for further analysis For more information, see the function documentation: - `?analyze_scientific_content` - Main analysis function with API integration - `?create_citation_network` - Interactive citation network visualization - `?calculate_readability_indices` - Readability metrics - `?calculate_word_distribution` - Word distribution analysis - `?get_crossref_references` - CrossRef API wrapper - `?parse_references_section` - Local reference parsing ### Additional Resources - CrossRef API: https://api.crossref.org - OpenAlex: https://openalex.org - Package repository: https://github.com/massimoaria/contentanalysis