88from . import ops , utils
99from .common import _maybe_promote
1010from .pycompat import iteritems , OrderedDict
11- from .utils import is_full_slice
11+ from .utils import is_full_slice , is_dict_like
1212from .variable import Variable , Coordinate , broadcast_variables
1313
1414
@@ -77,13 +77,33 @@ def align(*objects, **kwargs):
7777 aligned : same as *objects
7878 Tuple of objects with aligned coordinates.
7979 """
80+ return partial_align (* objects , exclude = None , ** kwargs )
81+
82+
83+ def partial_align (* objects , ** kwargs ):
84+ """partial_align(*objects, join='inner', copy=True, indexes=None,
85+ exclude=set())
86+
87+ Like align, but don't align along dimensions in exclude. Any indexes
88+ explicitly provided with the `indexes` argument should be used in preference
89+ to the aligned indexes.
90+
91+ Not public API.
92+ """
8093 join = kwargs .pop ('join' , 'inner' )
8194 copy = kwargs .pop ('copy' , True )
95+ indexes = kwargs .pop ('indexes' , None )
96+ exclude = kwargs .pop ('exclude' , None )
97+ if exclude is None :
98+ exclude = set ()
8299 if kwargs :
83100 raise TypeError ('align() got unexpected keyword arguments: %s'
84101 % list (kwargs ))
85102
86- joined_indexes = _join_indexes (join , objects )
103+ joined_indexes = _join_indexes (join , objects , exclude = exclude )
104+ if indexes is not None :
105+ joined_indexes .update (indexes )
106+
87107 result = []
88108 for obj in objects :
89109 valid_indexers = dict ((k , v ) for k , v in joined_indexes .items ()
@@ -92,36 +112,52 @@ def align(*objects, **kwargs):
92112 return tuple (result )
93113
94114
95- def partial_align (* objects , ** kwargs ):
96- """partial_align(*objects, join='inner', copy=True, exclude=set()
115+ def is_alignable (obj ):
116+ return hasattr (obj , 'indexes' ) and hasattr (obj , 'reindex' )
117+
97118
98- Like align, but don't align along dimensions in exclude. Not public API.
119+ def deep_align (list_of_variable_maps , join = 'outer' , copy = True , indexes = None ):
120+ """Align objects, recursing into dictionary values.
99121 """
100- join = kwargs .pop ('join' , 'inner' )
101- copy = kwargs .pop ('copy' , True )
102- exclude = kwargs .pop ('exclude' , set ())
103- assert not kwargs
104- joined_indexes = _join_indexes (join , objects , exclude = exclude )
105- return tuple (obj .reindex (copy = copy , ** joined_indexes ) for obj in objects )
122+ if indexes is None :
123+ indexes = {}
124+
125+ # We use keys to identify arguments to align. Integers indicate single
126+ # arguments, while (int, variable_name) pairs indicate variables in ordered
127+ # dictionaries.
128+ keys = []
129+ out = []
130+ targets = []
131+ sentinel = object ()
132+ for n , variables in enumerate (list_of_variable_maps ):
133+ if is_alignable (variables ):
134+ keys .append (n )
135+ targets .append (variables )
136+ out .append (sentinel )
137+ elif is_dict_like (variables ):
138+ for k , v in variables .items ():
139+ if is_alignable (v ) and k not in indexes :
140+ # don't align dict-like variables that are already fixed
141+ # indexes: we might be overwriting these index variables
142+ keys .append ((n , k ))
143+ targets .append (v )
144+ out .append (OrderedDict (variables ))
145+ else :
146+ out .append (variables )
106147
148+ aligned = partial_align (* targets , join = join , copy = copy , indexes = indexes )
107149
108- def align_variables (variables , join = 'outer' , copy = False ):
109- """Align all DataArrays in the provided dict, leaving other values alone.
110- """
111- from .dataarray import DataArray
112- from pandas import Series , DataFrame , Panel
113-
114- new_variables = OrderedDict (variables )
115- # if an item is a Series / DataFrame / Panel, try and wrap it in a DataArray constructor
116- new_variables .update ((
117- (k , DataArray (v )) for k , v in variables .items ()
118- if isinstance (v , (Series , DataFrame , Panel ))
119- ))
120-
121- alignable = [k for k , v in new_variables .items () if hasattr (v , 'indexes' )]
122- aligned = align (* [new_variables [a ] for a in alignable ], join = join , copy = copy )
123- new_variables .update (zip (alignable , aligned ))
124- return new_variables
150+ for key , aligned_obj in zip (keys , aligned ):
151+ if isinstance (key , tuple ):
152+ n , k = key
153+ out [n ][k ] = aligned_obj
154+ else :
155+ out [key ] = aligned_obj
156+
157+ # something went wrong: we should have replaced all sentinel values
158+ assert all (arg is not sentinel for arg in out )
159+
160+ return out
125161
126162
127163def reindex_variables (variables , indexes , indexers , method = None ,
0 commit comments