Skip to content
This repository has been archived by the owner on Jul 21, 2022. It is now read-only.

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
grahamfindlay committed May 29, 2021
0 parents commit e365085
Show file tree
Hide file tree
Showing 9 changed files with 333 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*.py[co]
*~
*.DS_Store
_build/
auto_examples/
gen_api/
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
## shablona
A bare-bones template for Python packages, ready for use with setuptools (PyPI), pip, and py.test.

### Using this as a template
Let's assume that you want to create a small scientific Python project called `smallish`.

To use this repository as a template, click the green "use this template" button on the front page of the "shablona" repository.

In "Repository name" enter the name of your project. For example, enter `smallish` here. After that, you can hit the "Create repository from template" button.

You should then be able to clone the new repo into your machine. You will want to change the names of the files. For example, you will want to move `shablona/shablona.py` to be called `smallish/smallish.py`
```
git mv shablona smallish
git mv smallish/shablona.py smallish/smallish.py
git mv smallish/tests/test_shablona.py smallish/tests/test_smallish.py
```

Make a commit recording these changes. Something like:
```
git commit -a -m "Moved names from `shablona` to `smallish`"
```

You will want to edit a few more places that still have `shablona` in them. Type the following to see where all these files are:
```
git grep shablona
```

You can replace `shablona` for `smallish` quickly with:
```
git grep -l 'shablona' | xargs sed -i 's/shablona/smallish/g'
```

Edit `shablona/__init__.py`, and `shablona/version.py` with the information specific to your project.

This very file (`README.md`) should be edited to reflect what your project is about.

At this point, make another commit, and continue to develop your own code based on this template.


### Contributing
If you wish to make any changes (e.g. add documentation, tests, continuous integration, etc.), please follow the [Shablona](https://github.com/uwescience/shablona) template.
11 changes: 11 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Per https://stackoverflow.com/questions/43658870/requirements-txt-vs-setup-py

# requirements.txt
#
# installs dependencies from ./setup.py, and the package itself,
# in editable mode
-e .

# (the -e above is optional). you could also just install the package
# normally with just the line below (after uncommenting)
# .
40 changes: 40 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import sys
import os
from setuptools import setup, find_packages
PACKAGES = find_packages()

# Get version and release info, which is all stored in shablona/version.py
ver_file = os.path.join('shablona', 'version.py')
with open(ver_file) as f:
exec(f.read())

# Give setuptools a hint to complain if it's too old a version
# 24.2.0 added the python_requires option
# Should match pyproject.toml
SETUP_REQUIRES = ['setuptools >= 24.2.0']
# This enables setuptools to install wheel on-the-fly
SETUP_REQUIRES += ['wheel'] if 'bdist_wheel' in sys.argv else []

opts = dict(name=NAME,
maintainer=MAINTAINER,
maintainer_email=MAINTAINER_EMAIL,
description=DESCRIPTION,
long_description=LONG_DESCRIPTION,
url=URL,
download_url=DOWNLOAD_URL,
license=LICENSE,
classifiers=CLASSIFIERS,
author=AUTHOR,
author_email=AUTHOR_EMAIL,
platforms=PLATFORMS,
version=VERSION,
packages=PACKAGES,
package_data=PACKAGE_DATA,
install_requires=REQUIRES,
python_requires=PYTHON_REQUIRES,
setup_requires=SETUP_REQUIRES,
requires=REQUIRES)


if __name__ == '__main__':
setup(**opts)
1 change: 1 addition & 0 deletions shablona/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .shablona import * # noqa
170 changes: 170 additions & 0 deletions shablona/shablona.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@

from __future__ import absolute_import, division, print_function
import numpy as np
import pandas as pd
import scipy.optimize as opt
from scipy.special import erf


def transform_data(data):
"""
Function that takes experimental data and gives us the
dependent/independent variables for analysis.
Parameters
----------
data : Pandas DataFrame or string.
If this is a DataFrame, it should have the columns `contrast1` and
`answer` from which the dependent and independent variables will be
extracted. If this is a string, it should be the full path to a csv
file that contains data that can be read into a DataFrame with this
specification.
Returns
-------
x : array
The unique contrast differences.
y : array
The proportion of '2' answers in each contrast difference
n : array
The number of trials in each x,y condition
"""
if isinstance(data, str):
data = pd.read_csv(data)

contrast1 = data['contrast1']
answers = data['answer']

x = np.unique(contrast1)
y = []
n = []

for c in x:
idx = np.where(contrast1 == c)
n.append(float(len(idx[0])))
answer1 = len(np.where(answers[idx[0]] == 1)[0])
y.append(answer1 / n[-1])
return x, y, n


def cumgauss(x, mu, sigma):
"""
The cumulative Gaussian at x, for the distribution with mean mu and
standard deviation sigma.
Parameters
----------
x : float or array
The values of x over which to evaluate the cumulative Gaussian function
mu : float
The mean parameter. Determines the x value at which the y value is 0.5
sigma : float
The variance parameter. Determines the slope of the curve at the point
of Deflection
Returns
-------
g : float or array
The cumulative gaussian with mean $\\mu$ and variance $\\sigma$
evaluated at all points in `x`.
Notes
-----
Based on:
http://en.wikipedia.org/wiki/Normal_distribution#Cumulative_distribution_function
The cumulative Gaussian function is defined as:
.. math::
\\Phi(x) = \\frac{1}{2} [1 + erf(\\frac{x}{\\sqrt{2}})]
Where, $erf$, the error function is defined as:
.. math::
erf(x) = \\frac{1}{\\sqrt{\\pi}} \\int_{-x}^{x} e^{t^2} dt
"""
return 0.5 * (1 + erf((x - mu) / (np.sqrt(2) * sigma)))


def opt_err_func(params, x, y, func):
"""
Error function for fitting a function using non-linear optimization.
Parameters
----------
params : tuple
A tuple with the parameters of `func` according to their order of
input
x : float array
An independent variable.
y : float array
The dependent variable.
func : function
A function with inputs: `(x, *params)`
Returns
-------
float array
The marginals of the fit to x/y given the params
"""
return y - func(x, *params)


class Model(object):
"""Class for fitting cumulative Gaussian functions to data"""
def __init__(self, func=cumgauss):
""" Initialize a model object.
Parameters
----------
data : Pandas DataFrame
Data from a subjective contrast judgement experiment
func : callable, optional
A function that relates x and y through a set of parameters.
Default: :func:`cumgauss`
"""
self.func = func

def fit(self, x, y, initial=[0.5, 1]):
"""
Fit a Model to data.
Parameters
----------
x : float or array
The independent variable: contrast values presented in the
experiment
y : float or array
The dependent variable
Returns
-------
fit : :class:`Fit` instance
A :class:`Fit` object that contains the parameters of the model.
"""
params, _ = opt.leastsq(opt_err_func, initial,
args=(x, y, self.func))
return Fit(self, params)


class Fit(object):
"""
Class for representing a fit of a model to data
"""
def __init__(self, model, params):
"""
Initialize a :class:`Fit` object.
Parameters
----------
model : a :class:`Model` instance
An object representing the model used
params : array or list
The parameters of the model evaluated for the data
"""
self.model = model
self.params = params

def predict(self, x):
"""
Predict values of the dependent variable based on values of the
indpendent variable.
Parameters
----------
x : float or array
Values of the independent variable. Can be values presented in
the experiment. For out-of-sample prediction (e.g. in
cross-validation), these can be values
that were not presented in the experiment.
Returns
-------
y : float or array
Predicted values of the dependent variable, corresponding to
values of the independent variable.
"""
return self.model.func(x, *self.params)
Empty file added shablona/tests/__init__.py
Empty file.
13 changes: 13 additions & 0 deletions shablona/tests/test_shablona.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import os.path as op
import numpy.testing as npt
import shablona as ece

data_path = op.join(ece.__path__[0], 'data')
#Load data like: op.join(data_path, 'mydatafile.dat')


def test_trivial():
"""
Should always pass. Just used to ensure that py.test is setup correctly.
"""
npt.assert_equal(np.array([1, 1, 1]), np.array([1, 1, 1]))
51 changes: 51 additions & 0 deletions shablona/version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from os.path import join as pjoin

# Format expected by setup.py and doc/source/conf.py: string of form "X.Y.Z"
_version_major = 0
_version_minor = 1
_version_micro = '' # use '' for first of series, number for 1 and above
_version_extra = 'dev'
# _version_extra = '' # Uncomment this for full releases

# Construct full version string from these.
_ver = [_version_major, _version_minor]
if _version_micro:
_ver.append(_version_micro)
if _version_extra:
_ver.append(_version_extra)

__version__ = '.'.join(map(str, _ver))

CLASSIFIERS = ["Development Status :: 3 - Alpha",
"Environment :: Console",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Topic :: Scientific/Engineering"]

# Description should be a one-liner:
description = "shablona: Put a one-liner description of your code here"
# Long description will go up on the pypi page
long_description = """
shablona: Put a longer description of your code here.
"""

NAME = "shablona"
MAINTAINER = "Graham Findlay"
MAINTAINER_EMAIL = "[email protected]"
DESCRIPTION = description
LONG_DESCRIPTION = long_description
URL = "http://github.com/CSC-UW/shablona"
DOWNLOAD_URL = ""
LICENSE = "MIT"
AUTHOR = "Graham Findlay"
AUTHOR_EMAIL = "[email protected]"
PLATFORMS = "OS Independent"
MAJOR = _version_major
MINOR = _version_minor
MICRO = _version_micro
VERSION = __version__
PACKAGE_DATA = {'shablona': [pjoin('data', '*')]}
REQUIRES = ["numpy"]
PYTHON_REQUIRES = ">= 3.7"

0 comments on commit e365085

Please sign in to comment.