NarrativeMapper is a text analysis pipeline that uncovers the dominant narratives and emotional tones within online communities.
Project description
NarrativeMapper
Overview:
The NarrativeMapper package is a discourse analysis pipeline that uncovers the dominant narratives and emotional tones within online communities.
This project processes textual messages from .csv files, then applies OpenAI’s embedding API (text-embedding-3-large) to convert each message into semantic vectors. These embeddings are clustered using UMAP for dimensionality reduction and HDBSCAN for density-based clustering.
For each discovered cluster, the tool:
-
Extracts the main talking points OpenAI Chat Completions
-
Analyzes the emotional tone using a Hugging Face sentiment classifier
-
Outputs structured summaries of the narrative + emotion pairs
Install via PyPI:
Example Output:
This example is based off of 1800 r/antiwork comments from the top 300 posts within the last year (Date of Writing: 2025-04-03).
Output using format_to_dict() function. Useful for JSON export.
click to view output example
{
'online_group_name': 'r/antiwork',
'clusters': [
{
'label': 'The core theme of this cluster revolves around frustrations and criticisms of modern job application processes, including exploitative practices, ineffective interviews, and the use of AI and personality tests that often discriminate against neurodiverse individuals.',
'tone': 'NEGATIVE',
'comment_count': 74
},
{
'label': 'The core themes of this cluster revolve around the challenges of low wages in the fast food and service industries, the rising cost of living, and the perceived disconnect between corporate profits and employee compensation.',
'tone': 'NEGATIVE',
'comment_count': 109
},
{
'label': 'The core theme of this cluster revolves around employee dissatisfaction with workplace policies, management practices, and the struggle for work-life balance, often highlighting issues of wage theft, lack of respect for personal time, and the negative impact of corporate culture on mental health.',
'tone': 'NEGATIVE',
'comment_count': 500
},
{
'label': "The core theme of this cluster revolves around the dissatisfaction with traditional work schedules, advocating for shorter workweeks and better work-life balance, while highlighting the negative impact of long hours and inadequate parental leave on individuals' well-being.",
'tone': 'NEGATIVE',
'comment_count': 83
},
{
'label': "The core theme of this cluster revolves around workers' struggles for fair wages, unionization, and collective action against corporate exploitation, particularly in the context of Boeing.",
'tone': 'NEGATIVE',
'comment_count': 56
},
{
'label': 'The comments primarily express strong criticism of Elon Musk and the corporate culture surrounding wealth accumulation, highlighting issues of exploitation, inequality, and the disconnect between CEOs and their employees.',
'tone': 'NEGATIVE',
'comment_count': 50
},
{
'label': 'The core theme of this cluster revolves around the critique of wealth inequality and capitalism, highlighting the exploitation of workers, the concentration of wealth among the elite, and the systemic issues that perpetuate economic disparity and social injustice.',
'tone': 'NEGATIVE',
'comment_count': 157
},
{
'label': 'The comments reflect widespread frustration and despair among younger generations regarding financial instability, lack of affordable housing, inadequate retirement planning, and the perception of being exploited in the workforce, often contrasting their struggles with the experiences of older generations.',
'tone': 'NEGATIVE',
'comment_count': 92
}
]
}
Other formatting functions are available. Both format_by_text() and format_by_cluster() return pandas DataFrames that are well-suited for CSV export.
format_by_cluster() example to showcase output format:
format_by_text() example output to showcase output format:
Pipeline Architecture:
CSV Text Data --> Embeddings (embeddings.py) --> Cluster (clustering.py) --> Summarize (summarize.py) --> Formatting (formatters.py)
embeddings.py: Converts textual messages into 3072 dimensional vectors (OPEN AI's text-embedding-3-large).
clustering.py: Clusters embedding vectors using UMAP for reduction and HDBSCAN for clustering.
summarize.py: Determines summaries/label-names (4o-gpt-mini Chat Completion) and sentiment (distilbert-base-uncased-finetuned-sst-2-english) for each cluster.
formatters.py: Formats summarized clusters into useful forms for data analysis.
How to Use:
Option 1: High-Level Class-Based Interface
click to view class-based usage
#initialize NarrativeMapper object
mapper = NarrativeMapper("r/antiwork")
#embeds semantic vectors
mapper.load_embeddings("path/to/your/file.csv")
#clustering: n_components, n_neighbors are UMAP variables. min_cluser_size, min_samples are HDBSCAN variables.
mapper.cluster(n_components=20, n_neighbors=20, min_cluster_size=40, min_samples=15)
#summarize each cluster's topic and sentiment
mapper.summarize()
#export in your preferred format
summary_dict = mapper.format_to_dict()
text_df = mapper.format_by_text()
cluster_df = mapper.format_by_cluster()
#saving DataFrames to csv
text_df.to_csv("comments_by_cluster.csv", index=False)
cluster_df.to_csv("cluster_summary.csv", index=False)
Option 2: Low-Level Functional Interface
click to view function-based usage
#manual control over each step:
embeddings = get_embeddings("path/to/your/file.csv")
cluster_df = cluster_embeddings(embeddings, n_components=20, n_neighbors=20, min_cluster_size=40, min_samples=15)
summary_df = summarize_clusters(cluster_df)
#export/format options
summary_dict = format_to_dict(summary_df, online_group_name="r/antiwork")
text_df = format_by_text(summary_df, online_group_name="r/antiwork")
cluster_df = format_by_cluster(summary_df, online_group_name="r/antiwork")
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file narrativemapper-0.1.1.tar.gz.
File metadata
- Download URL: narrativemapper-0.1.1.tar.gz
- Upload date:
- Size: 9.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2b22cc1b57c30e1033cc4fd3d2ce8af88cad691c6c0945a0f02fd86157b9168f
|
|
| MD5 |
6df7d74a4e5ba6d6f8571feeaf5deee3
|
|
| BLAKE2b-256 |
6de262316c0fa40411fac960fb4bb4a20e5dd85eb1f67b4896ce73285a8538d8
|
File details
Details for the file narrativemapper-0.1.1-py3-none-any.whl.
File metadata
- Download URL: narrativemapper-0.1.1-py3-none-any.whl
- Upload date:
- Size: 11.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
022ce3e987696b0ac4d2cc338798debfe41ba1e18822e1aa56d12e9d13587adf
|
|
| MD5 |
46ee1a99cce8de357f20ff43791eff8d
|
|
| BLAKE2b-256 |
fef190a4c6a405f8bcb2368fd395ca412f6336f878c7d0c012221454cc1481fb
|