-
Eva Schiffer authoredEva Schiffer authored
filters.py 12.30 KiB
#!/usr/bin/env python
# encoding: utf-8
"""
General filters that can be used to prefilter data before comparison (define in the config file).
Created by Eva Schiffer August 2009.
Copyright (c) 2009 University of Wisconsin SSEC. All rights reserved.
"""
import numpy as np
def trim_off_of_top (data, num_elements_to_trim) :
"""
Remove num_elements_to_trim rows from the top of the data array
and return a copy of the remaining data
"""
assert(num_elements_to_trim >= 0)
return data[num_elements_to_trim:, :].copy()
def trim_off_of_bottom (data, num_elements_to_trim) :
"""
Remove num_elements_to_trim rows from the bottom of the data array
and return a copy of the remaining data
"""
assert(num_elements_to_trim >= 0)
return data[:(-1 * num_elements_to_trim), :].copy()
def trim_off_of_right (data, num_elements_to_trim) :
"""
Remove num_elements_to_trim columns from the right side of the data array
and return a copy of the remaining data
"""
assert(num_elements_to_trim >= 0)
return data[:, :(-1 * num_elements_to_trim)].copy()
def trim_off_of_left (data, num_elements_to_trim) :
"""
Remove num_elements_to_trim columns from the left side of the data array
and return a copy of the remaining data
"""
assert(num_elements_to_trim >= 0)
return data[:, num_elements_to_trim:]
def reverse_2D_data_vertically (data) :
"""
Reverse two dimensional data along it's first dimension.
For most satellite data this will result in it flipping vertically
when glance displays it.
"""
return data.copy()[::-1]
def reverse_2D_data_horizontally (data) :
"""
Reverse two dimensional data along it's second dimension.
For most satellite data this will result in it flipping horizontally
when glance displays it.
"""
return data.copy()[:, ::-1]
def flatten_data_into_bins (data, ranges, new_values, missing_value, return_data_type) :
"""
Sort the data into the given ranges. Each range should correspond to a value
given in the list of new_values; this value will be used for all data points
that fall within that range. Ranges will be treated as a continuious spectrum
So please list them in increasing order.
Note: Data values that fall directly between two ranges will be grouped with
the larger range.
Also Note: If a data point is not found to fall within any of the ranges,
the missing_value will be filled into that spot instead.
"""
# make sure we have values to match each range, no more and no less
assert(len(ranges) == (len(new_values) + 1))
new_data = np.zeros(data.shape, dtype=return_data_type)
# find the data in each range
filled_mask = np.zeros(data.shape, dtype=bool)
temp_mask = np.zeros(data.shape, dtype=bool)
for index in range(len(ranges) - 1) :
temp_mask = (data >= ranges[index]) & (data <= ranges[index + 1])
new_data[temp_mask] = new_values[index]
filled_mask = filled_mask | temp_mask
# clean up anything that didn't get filled
new_data[~filled_mask] = missing_value
return new_data
def extract_bit_from_packed_mask (data, index_of_bit_to_extract,
(truth_value, false_value),
return_data_type) :
"""
Extract a one bit boolean mask from a larger packed data set. The bit that
you wish to extract must be identified by it's index from the lower end of
the array of bits (with 0 being the bit that would represent a value of 1
if the mask was an integer; so the 1 indexed bit would represent the integer
value of 2, the 2 indexed bit a value of 4, and so on).
Note: It is assumed that the endian-ness of your data was handled correctly
by whatever code loaded it from the file.
The return data will be filled with the truth_value and false_value based
on whether the bit was 1 (true) or 0 (false) and will be of the requested
return_data_type.
Note: If you wish to examine or compare more than one packed bit, you may
use this filter multiple times to extract each separately or you can use
extract_multiple_bits_from_packed_mask to extract a set of bits as combined
integers.
"""
# we are only allowing positive indexing due to data typing complexity
assert(index_of_bit_to_extract >= 0)
# make our mask
mask_value = 2**index_of_bit_to_extract
bit_mask = np.zeros(data.shape, dtype=int)
bit_mask = bit_mask + mask_value
# get the data out and fill in the requested values
pure_extraction = np.bitwise_and(data, bit_mask)
new_data = np.zeros(data.shape, dtype=return_data_type)
new_data[pure_extraction > 0] = truth_value
new_data[pure_extraction <= 0] = false_value
return new_data
def extract_multiple_bits_from_packed_mask (data, list_of_indices_to_extract) :
"""
Extract multiple bits packed into a larger data set. The bits that you wish
to extract must be identified by their indecides from the lower end of the
array of bits (with 0 being the bit that would represent a value of 1
if the mask was an integer; so the 1 indexed bit would represent the integer
value of 2, the 2 indexed bit a value of 4, and so on).
Note: It is assumed that the endian-ness of your data was handled correctly
by whatever code loaded it from the file.
The bits will be extracted and put back together from lowest index to highest.
They will be interpreted as integers.
"""
# we are only allowing positive indexing due to data typing complexity
assert(np.min(list_of_indices_to_extract) >= 0)
# make some empty variables for use in our loop
new_data = np.zeros(data.shape, dtype=np.int)
bit_mask = np.zeros(data.shape, dtype=np.int)
# pull out each bit and add that information to the return array
for list_idx in range(len(list_of_indices_to_extract)) :
# make a mask to use for extracting this bit
mask_value = 2**list_of_indices_to_extract[list_idx]
bit_mask *= 0
bit_mask += mask_value
# extract the bit
pure_extraction = np.bitwise_and(data, bit_mask)
# add the bit to our return array
new_data[pure_extraction > 0] += 2**list_idx
return new_data
def select_slice_from_3D_last (data, slice_index) :
"""
Select a slice from a 3 dimensional data set.
slice_index indicates the index of the slice that you would like returned
by this function
note: this assumes you wish to slice across the dimention that you would index
into last
"""
assert(slice_index >= 0)
assert(slice_index < data.shape[2])
return data[:, :, slice_index]
def rotate_indexes_right (data) :
"""
move the order of the indexes in the array to the right in order, taking the last one
and putting it in the first index spot
note: at the moment this filter only works with 3 dimentional data sets
"""
# figure out the new order of the indexes
numDims = len(data.shape)
newIndexOrder = [numDims - 1] + range(numDims - 1)
# reorder the data
data_new = data.transpose(newIndexOrder)
"""
# figure out the shapes we have/need
old_shape = data.shape
#print ('old shape: ' + str(old_shape))
new_shape = old_shape[-1:] + old_shape[:-1]
#print ('new shape: ' + str(new_shape))
# set up our new data
data_new = np.empty_like(data)
data_new = data_new.reshape(new_shape)
# move the old data into the new data shape
for index1 in range(old_shape[0]) :
for index2 in range(old_shape[1]) :
for index3 in range(old_shape[2]) :
data_new[index3, index1, index2] = data[index1, index2, index3]
"""
return data_new
# TODO, this method is only here for backwards compatibility
def set_to_value_between_bounds(data, value_to_set_to, bottom_bound_exclusive, top_bound_exclusive) :
"""
Wherever the data is non-finite or outside the given bounds, set it to the given value.
"""
return set_to_value_outside_bounds (data, value_to_set_to, bottom_bound_exclusive, top_bound_exclusive)
def set_to_value_outside_bounds(data, value_to_set_to, bottom_bound_exclusive, top_bound_exclusive) :
"""
Wherever the data is non-finite or outside the given bounds, set it to the given value.
"""
mask = (data < bottom_bound_exclusive) | (data > top_bound_exclusive) | (~ np.isfinite(data))
data[mask] = value_to_set_to
return data
def filter_based_on_additional_data_set_min_max_bounds(data, filterData, missingValue=None,
minOkFilterValue=None, maxOkFilterValue=None) :
"""
filter a data set based on values in another data set
if some of the filter data is above/below the optional min/max values the corresponding values in the
data will be set to the missingValue
ex. this filter might be used to remove winds data that has a quality index below a certain threshold
"""
assert(data.shape == filterData.shape)
goodAreas = np.ones(data.shape, dtype=bool)
if minOkFilterValue is not None :
goodAreas = goodAreas & (filterData >= minOkFilterValue)
if maxOkFilterValue is not None :
goodAreas = goodAreas & (filterData <= maxOkFilterValue)
newData = data.copy()
newData[~goodAreas] = missingValue
return newData
def organize_ipopp_data_into_image(original_ipopp_data, wave_number=None, missing_value=None,
propagate_partial_missing_values=False) :
"""
organize the ipopp data spatially into an 'image' of sorts
this basically consists of:
-> the 30 fields of regard
---------------
| | |
| | V
| | the 4 scan lines
| |
---------------
for each field of regard/scan line
_______
|_|_|_|
|_|_|_|
|_|_|_|
block of the 9 detectors
with the index to physical mapping:
0 1 2
3 4 5
6 7 8
for each detector point, if the wave_number was given
in the parameters, that specific interferogram data pt will be used
if no wave_number was given, the mean of the 717 pts will be used
"""
new_data_image = np.zeros((4 * 3, 30 * 3), dtype=original_ipopp_data.dtype)
# loop to the place in the old array to get each data point
for scan_line in range(4) :
for field_of_regard in range(30) :
for detector in range(9) :
# figure out the value we're moving
data_pt = None
data_array = original_ipopp_data[scan_line][field_of_regard][detector]
if (wave_number is not None):
data_pt = data_array[wave_number]
else:
if (propagate_partial_missing_values
and
sum(data_array == missing_value) > 0) :
data_pt = missing_value
else:
# remember to remove any remaining missing values
data_pt = np.mean(data_array[~(data_array == missing_value)])
# figure out where to put the value and put it there
index1 = scan_line * 3 + (detector / 3)
index2 = field_of_regard * 3 + (detector % 3)
new_data_image[index1][index2] = data_pt
return new_data_image
def get_sounding_profile_at_index(profile_data_3d, index_desired) :
"""
Select a level of the sounding profile data at the index given.
For example, if you wanted to select 300 hPa in the pressure profile, you would
enter an index of 64.
"""
assert(len(profile_data_3d.shape) > 1)
return profile_data_3d[index_desired].copy()