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

ENH: alignaxis function added #44

Merged
merged 7 commits into from Feb 7, 2012
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions doc/source/ref_flarry.rst
Expand Up @@ -31,6 +31,10 @@ The alignment functions help you align one of more larrys.

.. autofunction:: la.align_raw

------------

.. autofunction:: la.align_axis

------------

.. autofunction:: la.isaligned
Expand Down
69 changes: 66 additions & 3 deletions la/flarry.py
Expand Up @@ -7,9 +7,9 @@
from la.farray import covMissing
from la.missing import missing_marker, ismissing

__all__ = ['align', 'align_raw', 'isaligned', 'union', 'intersection',
'binaryop', 'add', 'sortby', 'subtract', 'multiply', 'divide',
'unique', 'stack', 'panel', 'cov', 'rand', 'randn']
__all__ = ['align', 'align_axis', 'align_raw', 'isaligned', 'union',
'intersection', 'binaryop', 'add', 'sortby', 'subtract', 'multiply',
'divide', 'unique', 'stack', 'panel', 'cov', 'rand', 'randn']


# Alignment -----------------------------------------------------------------
Expand Down Expand Up @@ -375,6 +375,69 @@ def align_raw(lar1, lar2, join='inner', cast=True):

return x1, x2, label, x1isview, x2isview

def align_axis(lars, axis=0, join='inner', flag=False):
"""
Align many larrys along potentially different axes.

Parameters
----------
lars : array_like
A collection (list, tuple, set, array, etc.) of larrys to align.
axis : {int, array_like}, optional
An integer indicating which axis along which to align the larrys in
`lars`, or a sequence of integers of the same length as `lars`
indicating which axis to use for each entry in `lars`.
join : {'inner', 'outer', 'left', 'right'}
Copy link
Owner

Choose a reason for hiding this comment

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

", optional"

If inner, then labels present in every larry will be kept. If 'outer',
Copy link
Owner

Choose a reason for hiding this comment

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

inner --> 'inner'

all labels appearing in any array are kept, and additional entries are
added to larrys containing fewer labels. See la.morph() for rules on
how this is done. If 'right' or 'left' then the labels of the output
will match those of either the first or last entry of `lars`,
respectively

Returns
-------
(lar1, lar2, ...) : tuple
Tuple of larrys, one corresponding to each entry of lars. None of the
output refer to input, and the labels of the output do not refer to one
another.

Copy link
Owner

Choose a reason for hiding this comment

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

This is a nice function. A couple of examples would be useful.

"""
# input checks and preprocessing
nlar = len(lars)
if isinstance(axis, int):
axis = np.repeat(axis, nlar)
if isinstance(axis, list) or isinstance(axis, tuple):
axis = np.array(axis)
Copy link
Owner

Choose a reason for hiding this comment

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

Why make axis into an array?

for j, lar in enumerate(lars):
if not isinstance(lar, larry):
raise ValueError("Inputs must be larry.")
if axis[j] > len(lar.shape):
raise ValueError("Axis out of range for input larry " + j)
if join not in ['inner', 'outer', 'left', 'right']:
raise ValueError("Value of `join` not recognized.")
# alignment
if join == 'left':
label = lars[0].label[axis[0]]
elif join == 'right':
label = lars[-1].label[axis[-1]]
else:
labels = [set(lar.label[axis[j]]) for j, lar in enumerate(lars)]
label = labels[0]
if join == 'inner':
for new_label in labels[1:]:
label &= new_label
elif join == 'outer':
for new_label in labels[1:]:
label |= new_label
if join in ('inner', 'outer'):
Copy link
Owner

Choose a reason for hiding this comment

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

Is this 'if statement' needed? Can

label = list(label); label.sort()

be put in the else above? Also can you put the two statements on two different lines instead of using ';'?

label = list(label); label.sort()
lars_out = []
for j, lar in enumerate(lars):
lab = list(label)
lars_out.append(lar.morph(lab, axis[j]))
return tuple(lars_out)

def isaligned(lar1, lar2, axis=None):
"""
Return True if labels of two given larrys are aligned along specified axis.
Expand Down
149 changes: 147 additions & 2 deletions la/tests/flarry_test.py
Expand Up @@ -9,7 +9,8 @@

from la import larry, rand
from la import (union, intersection, panel, stack, cov, align, isaligned,
binaryop, add, subtract, multiply, divide, unique, sortby)
binaryop, add, subtract, multiply, divide, unique, sortby,
align_axis)
from la.util.testing import assert_larry_equal as ale


Expand Down Expand Up @@ -515,7 +516,151 @@ def test_2d11(self):
ale(a1, d1, msg % 'left', original=y1)
ale(a2, d2, msg % 'right', original=y2)


class Test_align_axis(unittest.TestCase):
"Test align_axis on larrys"

def test_align_axis_1(self):
y1 = larry([[0.1, 0.2], [0.3, 0.4], [0.7, 0.8]])
y2 = larry([[0.1, 0.2, 0.5], [0.3, 0.4, 0.6]])
a1, a2 = align_axis([y1, y2], axis=1, join='outer')
d1 = larry([[0.1, 0.2, np.nan], [0.3, 0.4, np.nan],
[0.7, 0.8, np.nan]])
d2 = larry([[0.1, 0.2, 0.5], [0.3, 0.4, 0.6]])
msg = "align_axis fail on %s larry"
ale(a1, d1, msg % 'left', original=y1)
ale(a2, d2, msg % 'right', original=y2)

def test_align_axis_2(self):
"align 2d test #10"
y1 = larry([[1, 2], [3, 4], [7, 8]])
y2 = larry([[1, 2, 5], [3, 4, 6]])
a1, a2 = align_axis([y1, y2], axis=0, join='inner')
d1 = larry([[1, 2], [3, 4]])
d2 = larry([[1, 2, 5], [3, 4, 6]])
msg = "align_axis fail on %s larry"
ale(a1, d1, msg % 'left', original=y1)
ale(a2, d2, msg % 'right', original=y2)

def test_align_axis_3(self):
y1 = larry([1.0, 2.0, 3.0, 4.0], [[1, 2, 3, 4]])
y2 = larry([5.0, 6.0], [[2, 3]])
y3 = larry([7.0, 8.0, 9.0], [[2, 3, 5]])
a1, a2, a3 = align_axis([y1, y2, y3])
dlab = [[2, 3]]
d1 = larry([2.0, 3.0], dlab)
d2 = larry([5.0, 6.0], dlab)
d3 = larry([7.0, 8.0], dlab)
msg = "align_axis fail on %s larry"
ale(a1, d1, msg % '1st', original=y1)
ale(a2, d2, msg % '2nd', original=y2)
ale(a3, d3, msg % '3rd', original=y3)

def test_align_axis_4(self):
y1 = larry([1.0, 2.0, 3.0, 4.0], [[1, 2, 3, 4]])
y2 = larry([5.0, 6.0], [[2, 3]])
y3 = larry([7.0, 8.0, 9.0], [[2, 3, 5]])
a1, a2, a3 = align_axis([y1, y2, y3], join='outer')
dlab = [[1, 2, 3, 4, 5]]
d1 = larry([1.0, 2.0, 3.0, 4.0, nan], dlab)
d2 = larry([nan, 5.0, 6.0, nan, nan], dlab)
d3 = larry([nan, 7.0, 8.0, nan, 9.0], dlab)
msg = "align_axis fail on %s larry"
ale(a1, d1, msg % '1st', original=y1)
ale(a2, d2, msg % '2nd', original=y2)
ale(a3, d3, msg % '3rd', original=y3)

def test_align_axis_5(self):
y1 = larry([[1.0, 2.0],
[3.0, 4.0],
[5.0, 6.0]], [['a', 'b', 'c'], [1, 2]])
y2 = larry([['x'], ['y'], ['z']], [['a', 'b', 'cc'], [1]])
y3 = larry([50, 51, 52], [['aa', 'b', 'c']])
a1, a2, a3 = align_axis([y1, y2, y3], axis=0, join='inner')
d1 = larry([[3.0, 4.0]], [['b'], [1, 2]])
d2 = larry([['y']], [['b'], [1]])
d3 = larry([51], [['b']])
msg = "align_axis fail on %s larry"
ale(a1, d1, msg % '1st', original=y1)
ale(a2, d2, msg % '2nd', original=y2)
ale(a3, d3, msg % '3rd', original=y3)

def test_align_axis_6(self):
y1 = larry([[1.0, 2.0],
[3.0, 4.0],
[5.0, 6.0]], [['a', 'b', 'c'], [1, 2]])
y2 = larry([['x'], ['y'], ['z']], [['a', 'b', 'cc'], [1]])
y3 = larry([50, 51, 52], [['aa', 'b', 'c']])
a1, a2, a3 = align_axis([y1, y2, y3], axis=0, join='outer')
d1 = larry([[1.0, 2.0],
[nan, nan],
[3.0, 4.0],
[5.0, 6.0],
[nan, nan]],
label = [['a', 'aa', 'b', 'c', 'cc'], [1, 2]])
d2 = larry([['x'], [''], ['y'], [''], ['z']],
[['a', 'aa', 'b', 'c', 'cc'], [1]])
d3 = larry([nan, 50.0, 51.0, 52.0, nan], [['a', 'aa', 'b', 'c', 'cc']])
msg = "align_axis fail on %s larry"
ale(a1, d1, msg % '1st', original=y1)
ale(a2, d2, msg % '2nd', original=y2)
ale(a3, d3, msg % '3rd', original=y3)

def test_align_axis_7(self):
y1 = larry([[1.0, 2.0],
[3.0, 4.0],
[5.0, 6.0]], [['a', 'b', 'c'], [1, 2]])
y2 = larry([['x'], ['y'], ['z']], [['a', 'b', 'cc'], [1]])
y3 = larry([50, 51, 52], [['aa', 'b', 'c']])
a1, a2, a3 = align_axis([y1, y2, y3], axis=0, join='left')
d1 = larry([[1.0, 2.0],
[3.0, 4.0],
[5.0, 6.0]], [['a', 'b', 'c'], [1, 2]])
d2 = larry([['x'], ['y'], ['']],
[['a', 'b', 'c'], [1]])
d3 = larry([nan, 51.0, 52.0], [['a', 'b', 'c']])
msg = "align_axis fail on %s larry"
ale(a1, d1, msg % '1st', original=y1)
ale(a2, d2, msg % '2nd', original=y2)
ale(a3, d3, msg % '3rd', original=y3)

def test_align_axis_8(self):
y1 = larry([[1.0, 2.0],
[3.0, 4.0],
[5.0, 6.0]], [['a', 'b', 'c'], [1, 2]])
y2 = larry([['x'], ['y'], ['z']], [['a', 'b', 'cc'], [1]])
y3 = larry([50, 51, 52], [['aa', 'b', 'c']])
a3, a2, a1 = align_axis([y3, y2, y1], axis=0, join='right')
d1 = larry([[1.0, 2.0],
[3.0, 4.0],
[5.0, 6.0]], [['a', 'b', 'c'], [1, 2]])
d2 = larry([['x'], ['y'], ['']],
[['a', 'b', 'c'], [1]])
d3 = larry([nan, 51.0, 52.0], [['a', 'b', 'c']])
msg = "align_axis fail on %s larry"
ale(a1, d1, msg % '1st', original=y1)
ale(a2, d2, msg % '2nd', original=y2)
ale(a3, d3, msg % '3rd', original=y3)

def test_align_axis_9(self):
y1 = larry([[1.0, 2.0],
[3.0, 4.0],
[5.0, 6.0]], [['a', 'b', 'c'], [1, 2]])
y1 = y1.T
y2 = larry([['x'], ['y'], ['z']], [['a', 'b', 'cc'], [1]])
y3 = larry([50, 51, 52], [['aa', 'b', 'c']])
a1, a2, a3 = align_axis([y1, y2, y3], axis=[1, 0, 0], join='left')
d1 = larry([[1.0, 2.0],
[3.0, 4.0],
[5.0, 6.0]], [['a', 'b', 'c'], [1, 2]])
d1 = d1.T
d2 = larry([['x'], ['y'], ['']],
[['a', 'b', 'c'], [1]])
d3 = larry([nan, 51.0, 52.0], [['a', 'b', 'c']])
msg = "align_axis fail on %s larry"
ale(a1, d1, msg % '1st', original=y1)
ale(a2, d2, msg % '2nd', original=y2)
ale(a3, d3, msg % '3rd', original=y3)

class Test_binaryop(unittest.TestCase):
"Test la.binaryop()"

Expand Down