Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

ENH: alignaxis function added #44

Merged
merged 7 commits into from

2 participants

@stroxler

Closes #19 (sort of)

Note: I cannot find the problem, but there is a little style issue in the doc for align, align_raw, and align_axis. The html output has a colon out of place where PARAMETERS are. The isaligned() function's doc looks fine, but I cannot find any difference in the formatting, so I'm not sure.

la/flarry.py
@@ -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'}
+ If inner, then labels present in every larry will be kept. If 'outer',
@kwgoodman Owner

inner --> 'inner'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
la/flarry.py
((15 lines not shown))
+ indicating which axis to use for each entry in `lars`.
+ join : {'inner', 'outer', 'left', 'right'}
+ If inner, then labels present in every larry will be kept. If 'outer',
+ 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.
+
@kwgoodman Owner

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
la/flarry.py
((22 lines not shown))
+ 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.
+
+ """
+ # 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)
@kwgoodman Owner

Why make axis into an array?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
la/flarry.py
((44 lines not shown))
+ 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'):
@kwgoodman Owner

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 ';'?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@kwgoodman
Owner

This is a very handy function!

Can you update the unit test count in the readme? And add an entry in RELEASE.rst?

@kwgoodman kwgoodman commented on the diff
la/flarry.py
@@ -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'}
@kwgoodman Owner

", optional"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Steven Troxler added some commits
@kwgoodman kwgoodman merged commit 5f7027a into from
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Feb 6, 2012
  1. ENH Partial work for align_axis function.

    Steven Troxler authored
  2. ENH Code and finished basic tests, align_axis.

    Steven Troxler authored
  3. DOC Added documentation for align_axis.

    Steven Troxler authored
  4. STY Fixed a sphinx style issue in documentation.

    Steven Troxler authored
  5. STY better style on __all__

    Steven Troxler authored
Commits on Feb 7, 2012
  1. STY Code tweaks, RELEASE and README

    Steven Troxler authored
  2. STY missing quotes in doc, extra `if`

    Steven Troxler authored
This page is out of date. Refresh to see the latest.
View
4 README.rst
@@ -118,9 +118,9 @@ After you have installed ``la``, run the suite of unit tests::
>>> import la
>>> la.test()
<snip>
- Ran 3038 tests in 1.308s
+ Ran 3047 tests in 1.408s
OK
- <nose.result.TextTestResult run=3038 errors=0 failures=0>
+ <nose.result.TextTestResult run=3047 errors=0 failures=0>
The ``la`` package contains C extensions that speed up common alignment
operations such as adding two unaligned larrys. If the C extensions don't
View
1  RELEASE.rst
@@ -15,6 +15,7 @@ la 0.6
- la.isaligned() returns True if two larrys are aligned along specified axis
- la.sortby() sorts a larry by a row or column specified by its label
+- la.align_axis() aligns multiple larrys along (possibly) different axes
**Enhancements**
View
4 doc/source/ref_flarry.rst
@@ -32,6 +32,10 @@ The alignment functions help you align one of more larrys.
.. autofunction:: la.align_raw
------------
+
+.. autofunction:: la.align_axis
+
+------------
.. autofunction:: la.isaligned
View
155 la/flarry.py
@@ -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 -----------------------------------------------------------------
@@ -375,6 +375,155 @@ 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'}, optional
@kwgoodman Owner

", optional"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ If 'inner', then labels present in every larry will be kept. If
+ 'outer', 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.
+
+ Examples
+ --------
+
+ >>> l1 = la.larry([1, 2, 3, 4], [['a', 'b', 'c', 'd']])
+ >>> l2 = la.larry([[4, 5], [6, 7]], [['x', 'y'], ['c', 'd']])
+ >>> l3 = la.larry([8, 9, 10], [['c', 'd', 'e']])
+
+ Two arrays, with inner join:
+
+ >>> a1, a2 = la.align_axis([l1, l2], axis=[0, 1, 0])
+ >>> a1
+
+ label_0
+ c
+ d
+ x
+ array([3, 4])
+
+ >>> a2
+
+ label_0
+ x
+ y
+ label_1
+ c
+ d
+ x
+ array([[4, 5],
+ [6, 7]])
+
+ Two arrays, with outer join:
+
+ >>> a1, a2 = la.align_axis([l1, l3], join='outer')
+ >>> a1
+
+ label_0
+ a
+ b
+ c
+ d
+ e
+ x
+ array([ 1., 2., 3., 4., nan])
+
+ >>> a2
+
+ label_0
+ a
+ b
+ c
+ d
+ e
+ x
+ array([ nan, nan, 8., 9., 10.])
+
+
+ Multiple arrays:
+
+ >>> a1, a2 = la.align_axis([l1, l2, l3], axis=[0, 1, 0])
+ >>> a1
+
+ label_0
+ c
+ d
+ x
+ array([3, 4])
+
+ >>> a2
+
+ label_0
+ x
+ y
+ label_1
+ c
+ d
+ x
+ array([[4, 5],
+ [6, 7]])
+
+ >>> a3
+
+ label_0
+ c
+ d
+ x
+ array([8, 9])
+
+ """
+ # input checks and preprocessing
+ nlar = len(lars)
+ if isinstance(axis, int):
+ axis = [axis for j in range(nlar)]
+ 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 %d" % 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
+ label = list(label)
+ label.sort()
+ lars_out = []
+ # create output
+ 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.
View
149 la/tests/flarry_test.py
@@ -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
@@ -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()"
Something went wrong with that request. Please try again.