Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
__pycache__
*.egg-info

# Ignore because we're using this as a library.
uv.lock
21 changes: 21 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
install: ## Install as library
uv sync
uv pip install -e .

run: ## Run the example script
uv run python polislite/polislite.py


%:
@true

.PHONY: help

help:
@echo 'Usage: make <command>'
@echo
@echo 'where <command> is one of the following:'
@echo
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'

.DEFAULT_GOAL := help
28 changes: 26 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,35 @@ A lightweight Pol.is-like.

## Setup

pip install scikit-learn
- [Install][install-uv] `uv` Python package manager

[install-uv]: https://docs.astral.sh/uv/getting-started/installation/

## Usage

python polislite.py
This repo can be run as a self-contained example script, or used as a library.

### As A Library

This package can be installed as a library in another Python project using any package manager.

pip install git+https://github.com/patcon/polislite.git@python-package

uv add git+https://github.com/patcon/polislite.git@python-package
Comment on lines +19 to +21
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pip install git+https://github.com/patcon/polislite.git@python-package
uv add git+https://github.com/patcon/polislite.git@python-package
pip install git+https://github.com/eterps/polislite.git@main
uv add git+https://github.com/eterps/polislite.git@main


This also makes it simple to use in a Jupyter Notebook.

See sample notebook: [`polislite_library_usage.ipynb`][ipynb-example]

[ipynb-example]: /polislite_library_usage.ipynb

### Example script

uv run python polislite/polislite.py

### Development

Run `make` to see shortcut tasks for working on this project.

<details><summary>Output</summary>

Expand Down
Empty file added polislite/__init__.py
Empty file.
53 changes: 27 additions & 26 deletions polislite.py → polislite/polislite.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,31 +99,32 @@ def _generate_report(self, vote_matrix, clusters, statements):
stance = 'strongly agrees with' if opinion > 0 else 'strongly disagrees with'
print(f'- {stance}: {stmt}')

# Example usage
statements = [
'Climate change requires immediate action',
'Nuclear power is necessary for clean energy',
'Carbon tax should be implemented globally',
'Individual actions matter for sustainability',
'Companies should be held liable for emissions'
]
if __name__ == "__main__":
# Example usage
statements = [
'Climate change requires immediate action',
'Nuclear power is necessary for clean energy',
'Carbon tax should be implemented globally',
'Individual actions matter for sustainability',
'Companies should be held liable for emissions'
]

votes = [
# Group 1: Environmental purists (anti-nuclear)
['agree', 'disagree', 'agree', 'agree', 'agree'],
['agree', 'disagree', 'agree', 'agree', 'agree'],
['agree', 'disagree', 'agree', 'agree', 'agree'],

# Group 2: Tech-focused environmentalists (pro-nuclear)
['agree', 'agree', 'agree', 'disagree', 'agree'],
['agree', 'agree', 'agree', 'disagree', 'agree'],
['agree', 'agree', 'agree', 'disagree', 'agree'],

# Group 3: Business-oriented (anti-regulation)
['agree', 'agree', 'disagree', 'disagree', 'disagree'],
['agree', 'agree', 'disagree', 'disagree', 'disagree'],
['agree', 'agree', 'disagree', 'disagree', 'disagree']
]
votes = [
# Group 1: Environmental purists (anti-nuclear)
['agree', 'disagree', 'agree', 'agree', 'agree'],
['agree', 'disagree', 'agree', 'agree', 'agree'],
['agree', 'disagree', 'agree', 'agree', 'agree'],
# Group 2: Tech-focused environmentalists (pro-nuclear)
['agree', 'agree', 'agree', 'disagree', 'agree'],
['agree', 'agree', 'agree', 'disagree', 'agree'],
['agree', 'agree', 'agree', 'disagree', 'agree'],
# Group 3: Business-oriented (anti-regulation)
['agree', 'agree', 'disagree', 'disagree', 'disagree'],
['agree', 'agree', 'disagree', 'disagree', 'disagree'],
['agree', 'agree', 'disagree', 'disagree', 'disagree']
]

clusterer = PolisClusterer()
points, clusters = clusterer.analyze_opinions(votes, statements)
clusterer = PolisClusterer()
points, clusters = clusterer.analyze_opinions(votes, statements)
156 changes: 156 additions & 0 deletions polislite_library_usage.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"provenance": [],
"authorship_tag": "ABX9TyPLsyDH4WY7BEoL4VGciHd3",
"include_colab_link": true
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"language_info": {
"name": "python"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/github/patcon/polislite/blob/python-package/polislite_library_usage.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"<a href=\"https://colab.research.google.com/github/patcon/polislite/blob/python-package/polislite_library_usage.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
"<a href=\"https://colab.research.google.com/github/eterps/polislite/blob/main/polislite_library_usage.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"

]
},
{
"cell_type": "code",
"source": [
"!pip install git+https://github.com/patcon/polislite.git@python-package"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"!pip install git+https://github.com/patcon/polislite.git@python-package"
"!pip install git+https://github.com/eterps/polislite.git@main"

],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "9NebPULW1JTR",
"outputId": "0b95059b-d3b9-4840-8da3-8702b20318d9"
},
"execution_count": 1,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Collecting git+https://github.com/patcon/polislite.git@python-package\n",
" Cloning https://github.com/patcon/polislite.git (to revision python-package) to /tmp/pip-req-build-b8924s2s\n",
" Running command git clone --filter=blob:none --quiet https://github.com/patcon/polislite.git /tmp/pip-req-build-b8924s2s\n",
" Running command git checkout -b python-package --track origin/python-package\n",
" Switched to a new branch 'python-package'\n",
" Branch 'python-package' set up to track remote branch 'python-package' from 'origin'.\n",
" Resolved https://github.com/patcon/polislite.git to commit 9252f24c4acb6ea38a25c1b51b4546d087e91747\n",
Comment on lines +47 to +53
Copy link
Collaborator Author

@patcon patcon Jan 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"Collecting git+https://github.com/patcon/polislite.git@python-package\n",
" Cloning https://github.com/patcon/polislite.git (to revision python-package) to /tmp/pip-req-build-b8924s2s\n",
" Running command git clone --filter=blob:none --quiet https://github.com/patcon/polislite.git /tmp/pip-req-build-b8924s2s\n",
" Running command git checkout -b python-package --track origin/python-package\n",
" Switched to a new branch 'python-package'\n",
" Branch 'python-package' set up to track remote branch 'python-package' from 'origin'.\n",
" Resolved https://github.com/patcon/polislite.git to commit 9252f24c4acb6ea38a25c1b51b4546d087e91747\n",
"Collecting git+https://github.com/eterps/polislite.git@main\n",
" Cloning https://github.com/eterps/polislite.git (to revision main) to /tmp/pip-req-build-b8924s2s\n",
" Running command git clone --filter=blob:none --quiet https://github.com/eterps/polislite.git /tmp/pip-req-build-b8924s2s\n",
" Running command git checkout -b main --track origin/main\n",
" Switched to a new branch 'main'\n",
" Branch 'main' set up to track remote branch 'main' from 'origin'.\n",
" Resolved https://github.com/eterps/polislite.git to commit 9252f24c4acb6ea38a25c1b51b4546d087e91747\n",

" Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n",
" Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n",
" Preparing metadata (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n",
"Requirement already satisfied: scikit-learn>=1.6.0 in /usr/local/lib/python3.10/dist-packages (from polislite==0.1.0) (1.6.0)\n",
"Requirement already satisfied: numpy>=1.19.5 in /usr/local/lib/python3.10/dist-packages (from scikit-learn>=1.6.0->polislite==0.1.0) (1.26.4)\n",
"Requirement already satisfied: scipy>=1.6.0 in /usr/local/lib/python3.10/dist-packages (from scikit-learn>=1.6.0->polislite==0.1.0) (1.13.1)\n",
"Requirement already satisfied: joblib>=1.2.0 in /usr/local/lib/python3.10/dist-packages (from scikit-learn>=1.6.0->polislite==0.1.0) (1.4.2)\n",
"Requirement already satisfied: threadpoolctl>=3.1.0 in /usr/local/lib/python3.10/dist-packages (from scikit-learn>=1.6.0->polislite==0.1.0) (3.5.0)\n",
"Building wheels for collected packages: polislite\n",
" Building wheel for polislite (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n",
" Created wheel for polislite: filename=polislite-0.1.0-py3-none-any.whl size=4394 sha256=051e48ee7fc35772739a0611e047949170bec18ff9792890650db6a3faeaef37\n",
" Stored in directory: /tmp/pip-ephem-wheel-cache-3q6whqzs/wheels/0d/d5/a2/fe574e20315f0bdfc7f1d4b81bbf2b2caa4c7a4e2a9ef0511f\n",
"Successfully built polislite\n",
"Installing collected packages: polislite\n",
"Successfully installed polislite-0.1.0\n"
]
}
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "orSeaE2M1Il7",
"outputId": "ebcf7bef-1486-4379-830f-0e6d6eb3515a"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Consensus Statements:\n",
"- Climate change requires immediate action (strong agreement)\n",
"\n",
"Divisive Statements:\n",
"- Nuclear power is necessary for clean energy\n",
"- Carbon tax should be implemented globally\n",
"- Individual actions matter for sustainability\n",
"- Companies should be held liable for emissions\n",
"\n",
"Group Positions:\n",
"\n",
"Group 1 characteristics:\n",
"- strongly agrees with: Climate change requires immediate action\n",
"- strongly agrees with: Nuclear power is necessary for clean energy\n",
"- strongly disagrees with: Carbon tax should be implemented globally\n",
"- strongly disagrees with: Individual actions matter for sustainability\n",
"- strongly disagrees with: Companies should be held liable for emissions\n",
"\n",
"Group 2 characteristics:\n",
"- strongly agrees with: Climate change requires immediate action\n",
"- strongly agrees with: Nuclear power is necessary for clean energy\n",
"- strongly agrees with: Carbon tax should be implemented globally\n",
"- strongly disagrees with: Individual actions matter for sustainability\n",
"- strongly agrees with: Companies should be held liable for emissions\n",
"\n",
"Group 3 characteristics:\n",
"- strongly agrees with: Climate change requires immediate action\n",
"- strongly disagrees with: Nuclear power is necessary for clean energy\n",
"- strongly agrees with: Carbon tax should be implemented globally\n",
"- strongly agrees with: Individual actions matter for sustainability\n",
"- strongly agrees with: Companies should be held liable for emissions\n"
]
}
],
"source": [
"from polislite.polislite import PolisClusterer\n",
"\n",
"# Example usage\n",
"statements = [\n",
" 'Climate change requires immediate action',\n",
" 'Nuclear power is necessary for clean energy',\n",
" 'Carbon tax should be implemented globally',\n",
" 'Individual actions matter for sustainability',\n",
" 'Companies should be held liable for emissions'\n",
"]\n",
"\n",
"votes = [\n",
" # Group 1: Environmental purists (anti-nuclear)\n",
" ['agree', 'disagree', 'agree', 'agree', 'agree'],\n",
" ['agree', 'disagree', 'agree', 'agree', 'agree'],\n",
" ['agree', 'disagree', 'agree', 'agree', 'agree'],\n",
"\n",
" # Group 2: Tech-focused environmentalists (pro-nuclear)\n",
" ['agree', 'agree', 'agree', 'disagree', 'agree'],\n",
" ['agree', 'agree', 'agree', 'disagree', 'agree'],\n",
" ['agree', 'agree', 'agree', 'disagree', 'agree'],\n",
"\n",
" # Group 3: Business-oriented (anti-regulation)\n",
" ['agree', 'agree', 'disagree', 'disagree', 'disagree'],\n",
" ['agree', 'agree', 'disagree', 'disagree', 'disagree'],\n",
" ['agree', 'agree', 'disagree', 'disagree', 'disagree']\n",
"]\n",
"\n",
"clusterer = PolisClusterer()\n",
"points, clusters = clusterer.analyze_opinions(votes, statements)"
]
}
]
}
9 changes: 9 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[project]
name = "polislite"
version = "0.1.0"
description = "A lightweight Polis-like"
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"scikit-learn>=1.6.0",
]