Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Used vectorization to make the code faster #32

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
49 changes: 25 additions & 24 deletions Python-module/SpatialDE/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,61 +3,62 @@


def qvalue(pv, pi0=None):
'''
Estimates q-values from p-values
"""
Estimates q-values from p-values.

This function is modified based on https://github.com/nfusi/qvalue
Args:
pv (ndarray): Array of p-values.
pi0 (float): If None, it's estimated as suggested in Storey and Tibshirani, 2003.

Args
====
pi0: if None, it's estimated as suggested in Storey and Tibshirani, 2003.
Returns:
ndarray: Array of q-values with the same shape as pv.

'''
assert(pv.min() >= 0 and pv.max() <= 1), "p-values should be between 0 and 1"
Raises:
ValueError: If pv is not a NumPy array or has invalid values.
ValueError: If pi0 is not between 0 and 1.

"""
if not isinstance(pv, sp.ndarray):
raise ValueError("pv should be a NumPy array")

if not (pv.min() >= 0 and pv.max() <= 1):
raise ValueError("p-values should be between 0 and 1")

original_shape = pv.shape
pv = pv.ravel() # flattens the array in place, more efficient than flatten()
pv = pv.ravel()

m = float(len(pv))

# if the number of hypotheses is small, just set pi0 to 1
if len(pv) < 100 and pi0 is None:
pi0 = 1.0
elif pi0 is not None:
pi0 = pi0
else:
# evaluate pi0 for different lambdas
pi0 = []
lam = sp.arange(0, 0.90, 0.01)
counts = sp.array([(pv > i).sum() for i in sp.arange(0, 0.9, 0.01)])
for l in range(len(lam)):
pi0.append(counts[l]/(m*(1-lam[l])))

pi0 = sp.array(pi0)
lam = sp.arange(0, 0.90, 0.01)
pi0 = counts / (m * (1 - lam))

# fit natural cubic spline
tck = interpolate.splrep(lam, pi0, k=3)
pi0 = interpolate.splev(lam[-1], tck)

if pi0 > 1:
pi0 = 1.0

assert(pi0 >= 0 and pi0 <= 1), "pi0 is not between 0 and 1: %f" % pi0
if not (0 <= pi0 <= 1):
raise ValueError("pi0 is not between 0 and 1: %f" % pi0)

p_ordered = sp.argsort(pv)
pv = pv[p_ordered]
qv = pi0 * m/len(pv) * pv
qv = pi0 * m / len(pv) * pv
qv[-1] = min(qv[-1], 1.0)

for i in range(len(pv)-2, -1, -1):
qv[i] = min(pi0*m*pv[i]/(i+1.0), qv[i+1])
i = sp.arange(len(pv) - 2, -1, -1)
qv[i] = sp.minimum(pi0 * m * pv[i] / (i + 1.0), qv[i + 1])

# reorder qvalues
qv_temp = qv.copy()
qv = sp.zeros_like(qv)
qv[p_ordered] = qv_temp

# reshape qvalues
qv = qv.reshape(original_shape)

return qv