Skip to content

Commit

Permalink
Merge pull request #1 from CyberAgentAILab/feat/setup
Browse files Browse the repository at this point in the history
Setup base file structures for Python package
  • Loading branch information
TomeHirata authored Jun 17, 2024
2 parents ca7d927 + 319e5f3 commit 2b1e216
Show file tree
Hide file tree
Showing 15 changed files with 1,123 additions and 0 deletions.
34 changes: 34 additions & 0 deletions .github/workflows/build-doc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Build Docs

on:
push:
branches:
- main

jobs:
build-docs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install sphinx sphinx_rtd_theme
- name: Build documentation
run: |
cd docs
make html
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs/build/html
37 changes: 37 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Test package

on:
push:
branches:
- main
pull_request:

jobs:
lint-and-test:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'

- name: Install pipenv
run: |
python -m pip install --upgrade pip
pip install pipenv
- name: Install dependencies
run: |
pipenv install --dev
- name: Lint with ruff
run: |
pipenv run lint
- name: Run unit tests
run: |
pipenv run unittest
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,8 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

.ruff_cache

# Mac OS
.DS_Store
22 changes: 22 additions & 0 deletions Pipfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]
numpy = "~=1.26"
matplotlib = "~=3.7"

[dev-packages]
build = "~=1.2.1"
ruff = "~=0.4.9"
sphinx = "~=7.3.7"

[requires]
python_version = "3.11"

[scripts]
format = "ruff format"
lint = "ruff check"
lint-fix = "ruff check --fix"
unittest = "python -m unittest"
774 changes: 774 additions & 0 deletions Pipfile.lock

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions docs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#

# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build

# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
35 changes: 35 additions & 0 deletions docs/make.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
@ECHO OFF

pushd %~dp0

REM Command file for Sphinx documentation

if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build

%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.https://www.sphinx-doc.org/
exit /b 1
)

if "%1" == "" goto help

%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end

:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%

:end
popd
31 changes: 31 additions & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import os
import sys

sys.path.insert(0, os.path.abspath("../../"))
# Configuration file for the Sphinx documentation builder.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html

# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information

project = "dte_adj"
copyright = "2024, CyberAgent, Inc."
author = "CyberAgent, Inc"
release = "0.1.0"

# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration

extensions = ["sphinx.ext.autodoc", "sphinx.ext.napoleon", "sphinx.ext.viewcode"]

templates_path = ["_templates"]
exclude_patterns = []


# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output

html_theme = "alabaster"
html_static_path = ["_static"]
20 changes: 20 additions & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.. dte_adj documentation master file, created by
sphinx-quickstart on Tue Jun 18 01:04:59 2024.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to dte_adj's documentation!
===================================

.. toctree::
:maxdepth: 2
:caption: Contents:

modules

Indices and tables
==================

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
15 changes: 15 additions & 0 deletions docs/source/modules.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Modules
==================

.. toctree::
:maxdepth: 2

.. automodule:: dte_adj
:members:
:undoc-members:
:show-inheritance:

.. automodule:: dte_adj.plot
:members:
:undoc-members:
:show-inheritance:
Empty file added dte_adj/__init__.py
Empty file.
54 changes: 54 additions & 0 deletions dte_adj/plot/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.axis as axis
from typing import Optional


def plot(
x_values: np.ndarray,
y_values: np.ndarray,
upper_bands: np.ndarray,
lower_bands: np.ndarray,
ax: Optional[axis.Axis] = None,
title: Optional[str] = None,
xlabel: Optional[str] = None,
ylabel: Optional[str] = None,
):
"""Visualize distributional parameters and their confidence intervals.
Args:
x_values (np.Array): values to be used for x axis.
y_values (np.Array): Expected distributional parameters.
upper_bands (np.Array): Upper band for the distributional parameters.
lower_bands (np.Array): Lower band for the distributional parameters.
ax (matplotlib.axes.Axes, optional): Target axes instance. If None, a new figure and axes will be created.
title (str, optional): Axes title.
xlabel (str, optional): X-axis title label.
ylabel (str, optional): Y-axis title label.
Returns:
matplotlib.axes.Axes: The axes with the plot.
"""
if ax is None:
fig, ax = plt.subplots()

ax.plot(x_values, y_values, label="Values", color="blue")
ax.fill_between(
x_values,
lower_bands,
upper_bands,
color="gray",
alpha=0.3,
label="Confidence Interval",
)

if title is not None:
ax.set_title(title)
if xlabel is not None:
ax.set_xlabel(xlabel)
if ylabel is not None:
ax.set_ylabel(ylabel)

ax.legend()

return ax
23 changes: 23 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "dte_adj"
version = "0.1.0"
description = "This is a Python library for a research paper 'Estimating Distributional Treatment Effects in Randomized Experiments: Machine Learning for Variance Reduction'"
readme = "README.md"
requires-python = ">=3.6"
license = {text = "MIT"}
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent"
]
dependencies = [
"numpy~=1.26",
"matplotlib~=3.7"
]

[project.urls]
homepage = "https://github.com/CyberAgentAILab/python-dte-adjustment"
Empty file added tests/__init__.py
Empty file.
53 changes: 53 additions & 0 deletions tests/test_plot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import unittest
from unittest.mock import patch, MagicMock
import numpy as np
from dte_adj.plot import plot


class TestPlot(unittest.TestCase):
@patch("dte_adj.plot.plt")
def test_plot(self, mock_plt):
# Arrange
x_values = np.array([1, 2, 3, 4, 5])
y_values = np.array([1, 2, 3, 4, 5])
upper_bands = np.array([2, 3, 4, 5, 6])
lower_bands = np.array([0, 1, 2, 3, 4])
mock_ax = MagicMock()
mock_plt.subplots.return_value = (MagicMock(), mock_ax)

# Act
result_ax = plot(
x_values,
y_values,
upper_bands,
lower_bands,
title="Test Title",
xlabel="X Axis",
ylabel="Y Axis",
)

# Assert
self.assertEqual(result_ax, mock_ax)
mock_plt.subplots.assert_called_once()
plot_call = mock_ax.plot.call_args
fill_between_call = mock_ax.fill_between.call_args
plot_args, plot_kwargs = plot_call
x_values_arg, y_values_arg = plot_args
self.assertTrue(np.array_equal(x_values_arg, x_values))
self.assertTrue(np.array_equal(y_values_arg, y_values))
fill_between_args, fill_between_kwargs = fill_between_call
x_fill, lower_fill, upper_fill = fill_between_args
self.assertTrue(np.array_equal(x_fill, x_values_arg))
self.assertTrue(np.array_equal(lower_fill, lower_bands))
self.assertTrue(np.array_equal(upper_fill, upper_bands))
self.assertEqual(fill_between_kwargs["color"], "gray")
self.assertAlmostEqual(fill_between_kwargs["alpha"], 0.3)
self.assertEqual(fill_between_kwargs["label"], "Confidence Interval")
mock_ax.set_title.assert_called_once_with("Test Title")
mock_ax.set_xlabel.assert_called_once_with("X Axis")
mock_ax.set_ylabel.assert_called_once_with("Y Axis")
mock_ax.legend.assert_called_once()


if __name__ == "__main__":
unittest.main()

0 comments on commit 2b1e216

Please sign in to comment.