def append_column_to_array(array, new_column, new_values, other_cols=None): """ Adds a new column to a record array Parameters ---------- array : numpy record array new_column : str The name of the new column to be created new_values : scalar or sequence The value or array of values to be inserted into the new column. other_cols : sequence of str, optional A subset of exististing columns that will be kept in the final array. If not provided, all existing columns are retained. Returns ------- new_array : numpy record array The new array with the new column. """ from numpy.lib.recfunctions import append_fields # validate and convert the new column to a list to work # with the numpy API new_column = validate.non_empty_list(new_column) # validate and select out all of the "other" columns if other_cols is not None: other_cols = validate.non_empty_list(other_cols) if new_column in other_cols: msg = "`new_column` can not be the name of an existing column." raise ValueError(msg) else: # this raises a nasty warning in numpy even though this is the # way the warning says we should do this: with warnings.catch_warnings(record=True) as w: # pragma: no cover warnings.simplefilter("ignore") array = array[other_cols].copy() # make sure we don't have any unicode column names col_names = numpy.array(array.dtype.names) array.dtype.names = [ cn.encode('ascii', 'ignore') for cn in col_names ] # convert the new value to an array if necessary if numpy.isscalar(new_values): new_values = numpy.array([new_values] * array.shape[0]) # append the new colum new_array = append_fields(array, new_column, [new_values]) return new_array.data
def analyze(self, **params): """ Propagates water quality scores from monitoring locations to upstream subcatchments. Calls directly to :func:`propagate`. """ # analysis options ws = params.pop('workspace', '.') overwrite = params.pop('overwrite', True) add_output_to_map = params.pop('add_output_to_map', False) output_layer = params.pop('output_layer', None) # subcatchment info sc = params.pop('subcatchments', None) ID_col = params.pop('ID_column', None) downstream_ID_col = params.pop('downstream_ID_column', None) # monitoring location info ml = params.pop('monitoring_locations', None) ml_type_col = params.pop('ml_type_col', None) included_ml_types = validate.non_empty_list(params.pop( 'included_ml_types', None), on_fail='create') # monitoring location type filter function if ml_type_col is not None and len(included_ml_types) > 0: ml_filter = lambda row: row[ml_type_col] in included_ml_types else: ml_filter = None # value columns and aggregations value_cols_string = params.pop('value_columns', None) value_columns = [ vc.split(' ') for vc in value_cols_string.replace(' #', ' average').split(';') ] # streams data streams = params.pop('streams', None) # perform the analysis with utils.WorkSpace(ws), utils.OverwriteState(overwrite): output_layers = propagate( subcatchments=sc, id_col=ID_col, ds_col=downstream_ID_col, monitoring_locations=ml, ml_filter=ml_filter, ml_filter_cols=ml_type_col, value_columns=value_columns, output_path=output_layer, streams=streams, verbose=True, asMessage=True, ) if add_output_to_map: for lyr in output_layers: self._add_to_map(lyr) return output_layers
def populate_field(table, value_fxn, valuefield, keyfields=None): """ Loops through the records of a table and populates the value of one field (`valuefield`) based on another field (`keyfield`) by passing the entire row through a function (`value_fxn`). Relies on `arcpy.da.UpdateCursor`_. .. _arcpy.da.UpdateCursor: http://goo.gl/sa3mW6 Parameters ---------- table : Layer, table, or file path This is the layer/file that will have a new field created. value_fxn : callable Any function that accepts a row from an `arcpy.da.SearchCursor` and returns a *single* value. valuefield : string The name of the field to be computed. keyfields : list of str, optional The other fields that need to be present in the rows of the cursor. Returns ------- None .. note:: In the row object, the `valuefield` will be the last item. In other words, `row[0]` will return the first values in `*keyfields` and `row[-1]` will return the existing value of `valuefield` in that row. Examples -------- >>> # populate field ("Company") with a constant value ("Geosyntec") >>> populate_field("wetlands.shp", lambda row: "Geosyntec", "Company") """ fields = validate.non_empty_list(keyfields, on_fail='create') fields.append(valuefield) check_fields(table, *fields, should_exist=True) with arcpy.da.UpdateCursor(table, fields) as cur: for row in cur: row[-1] = value_fxn(row) cur.updateRow(row)
def aggregate_geom(layerpath, by_fields, field_stat_tuples, outputpath=None, **kwargs): """ Aggregates features class geometries into multipart geometries based on columns in attributes table. Basically this is a groupby operation on the attribute table, and the geometries are simply combined for aggregation. Other fields can but statistically aggregated as well. Parameters ---------- layerpath : str Name of the input feature class. by_fields : list of str The fields in the attribute table on which the records will be aggregated. field_stat_tuples : list of tuples of str List of two-tuples where the first element element is a field in the atrribute and the second element is how that column should be aggreated. .. note :: Statistics that are available are limited to those supported by `arcpy.management.Dissolve`. Those are: "FIRST", "LAST", "SUM", "MEAN", "MIN", "MAX", "RANGE", "STD", and "COUNT". outputpath : str, optional Name of the new feature class where the output should be saved. **kwargs Additional parameters passed to `arcpy.management.Dissolve. Returns ------- outputpath : str, optional Name of the new feature class where the output was sucessfully saved. Examples -------- >>> from propagator import utils >>> with utils.WorkSpace('C:/SOC/data.gdb'): ... utils.aggregate_geom( ... layerpath='streams_with_WQ_scores', ... by_fields=['Catch_ID', 'DS_Catch_ID'], ... field_stat_tuples=[('Dry_Metals', 'max'), ('Wet_Metals', 'min')], ... outputpath='agg_streams' ... ) """ by_fields = validate.non_empty_list(by_fields) arcpy.management.Dissolve(in_features=layerpath, out_feature_class=outputpath, dissolve_field=by_fields, statistics_fields=field_stat_tuples, **kwargs) return outputpath
def test_empty_creates(self): nt.assert_list_equal(validate.non_empty_list([], on_fail='create'), [])
def test_empty_list_raises(self): validate.non_empty_list([])
def test_None_raises(self): validate.non_empty_list(None)
def test_scalar(self): x = 1 nt.assert_list_equal(validate.non_empty_list(x), [x])
def test_baseline(self): x = [1, 2, 3] nt.assert_list_equal(validate.non_empty_list(x), x)