Fixed bugs in pandas dataframe construction for AggregateFilter

This commit is contained in:
wbinventor@gmail.com 2016-02-08 00:36:49 -05:00
parent 077eff7f19
commit 184ed3733b
5 changed files with 146 additions and 51 deletions

View file

@ -1,5 +1,7 @@
import sys
import copy
from numbers import Integral
from collections import Iterable
import numpy as np
@ -430,7 +432,7 @@ class CrossFilter(object):
filter_index = left_index * self.right_filter.num_bins + right_index
return filter_index
def get_pandas_dataframe(self, datasize, summary=None):
def get_pandas_dataframe(self, data_size, summary=None):
"""Builds a Pandas DataFrame for the CrossFilter's bins.
This method constructs a Pandas DataFrame object for the CrossFilter
@ -445,7 +447,7 @@ class CrossFilter(object):
Parameters
----------
datasize : Integral
data_size : Integral
The total number of bins in the tally corresponding to this filter
summary : None or Summary
An optional Summary object to be used to construct columns for
@ -472,12 +474,12 @@ class CrossFilter(object):
# If left and right filters are identical, do not combine bins
if self.left_filter == self.right_filter:
df = self.left_filter.get_pandas_dataframe(datasize, summary)
df = self.left_filter.get_pandas_dataframe(data_size, summary)
# If left and right filters are different, combine their bins
else:
left_df = self.left_filter.get_pandas_dataframe(datasize, summary)
right_df = self.right_filter.get_pandas_dataframe(datasize, summary)
left_df = self.left_filter.get_pandas_dataframe(data_size, summary)
right_df = self.right_filter.get_pandas_dataframe(data_size, summary)
left_df = left_df.astype(str)
right_df = right_df.astype(str)
df = '(' + left_df + ' ' + self.binary_op + ' ' + right_df + ')'
@ -713,6 +715,13 @@ class AggregateFilter(object):
def __ne__(self, other):
return not self == other
def __gt__(self, other):
# FIXME
return False
def __lt__(self, other):
return not self > other
def __repr__(self):
string = 'AggregateFilter\n'
string += '{0: <16}{1}{2}\n'.format('\tType', '=\t', self.type)
@ -757,7 +766,7 @@ class AggregateFilter(object):
@property
def num_bins(self):
return 1 if self.aggregate_filter else 0
return len(self.bins) if self.aggregate_filter else 0
@property
def stride(self):
@ -779,8 +788,10 @@ class AggregateFilter(object):
@bins.setter
def bins(self, bins):
cv.check_iterable_type('bins', bins, (Integral, tuple))
self._bins = bins
cv.check_iterable_type('bins', bins, Iterable)
self._bins = []
for bin in bins:
self._bins.append(tuple(bin))
@aggregate_op.setter
def aggregate_op(self, aggregate_op):
@ -825,9 +836,9 @@ class AggregateFilter(object):
'"{0}" is not one of the bins'.format(filter_bin)
raise ValueError(msg)
else:
return 0
return self.bins.index(filter_bin)
def get_pandas_dataframe(self, datasize, summary=None):
def get_pandas_dataframe(self, data_size, summary=None):
"""Builds a Pandas DataFrame for the AggregateFilter's bins.
This method constructs a Pandas DataFrame object for the AggregateFilter
@ -836,7 +847,7 @@ class AggregateFilter(object):
Parameters
----------
datasize : Integral
data_size : Integral
The total number of bins in the tally corresponding to this filter
summary : None or Summary
An optional Summary object to be used to construct columns for
@ -864,14 +875,85 @@ class AggregateFilter(object):
import pandas as pd
# Construct a sring representing the filter aggregation
aggregate_bin = '{0}('.format(self.aggregate_op)
aggregate_bin += ', '.join(map(str, self.bins)) + ')'
# Create NumPy array of the bin tuples for repeating / tiling
filter_bins = np.empty(self.num_bins, dtype=tuple)
for i, bin in enumerate(self.bins):
filter_bins[i] = bin
# Construct NumPy array of bin repeated for each element in dataframe
aggregate_bin_array = np.array([aggregate_bin])
aggregate_bin_array = np.repeat(aggregate_bin_array, datasize)
# Repeat and tile bins as needed for DataFrame
filter_bins = np.repeat(filter_bins, self.stride)
tile_factor = data_size / len(filter_bins)
filter_bins = np.tile(filter_bins, tile_factor)
# Construct Pandas DataFrame for the AggregateFilter
df = pd.DataFrame({self.type: aggregate_bin_array})
# Create DataFrame with aggregated bins
df = pd.DataFrame({self.type: filter_bins})
return df
def can_merge(self, other):
"""Determine if AggregateFilter can be merged with another.
Parameters
----------
other : AggregateFilter
Filter to compare with
Returns
-------
bool
Whether the filter can be merged
"""
if not isinstance(other, AggregateFilter):
return False
# Filters must be of the same type
elif self.type != other.type:
return False
# None of the bins in this filter should match in the other filter
for bin in self.bins:
if bin in other.bins:
return False
# None of the bins in the other filter should match in this filter
for bin in other.bins:
if bin in self.bins:
return False
# If all conditional checks passed then filters are mergeable
return True
def merge(self, other):
"""Merge this aggregatefilter with another.
Parameters
----------
other : AggregateFilter
Filter to merge with
Returns
-------
merged_filter : AggregateFilter
Filter resulting from the merge
"""
if not self.can_merge(other):
msg = 'Unable to merge "{0}" with "{1}" ' \
'filters'.format(self.type, other.type)
raise ValueError(msg)
# Create deep copy of filter to return as merged filter
merged_filter = copy.deepcopy(self)
# Merge unique filter bins
merged_bins = self.bins + other.bins
# Sort energy bin edges
if 'energy' in self.type:
merged_bins = sorted(merged_bins)
# Assign merged bins to merged filter
merged_filter.bins = list(merged_bins)
return merged_filter

View file

@ -263,11 +263,11 @@ class Filter(object):
return False
# Filters must be of the same type
elif self.type != other.type:
if self.type != other.type:
return False
# Distribcell filters cannot have more than one bin
elif self.type == 'distribcell':
if self.type == 'distribcell':
return False
# Mesh filters cannot have more than one bin
@ -289,6 +289,24 @@ class Filter(object):
else:
return True
'''
# FIXME: Should all bins be completely separate???
# FIMXE: This is necessary if merging will choose out unique bins
else:
# None of the bins in this filter should match in the other filter
for bin in self.bins:
if bin in other.bins:
return False
# None of the bins in the other filter should match in this filter
for bin in other.bins:
if bin in self.bins:
return False
# If all conditional checks pass then filters are mergeable
return True
'''
def merge(self, other):
"""Merge this filter with another.
@ -313,7 +331,8 @@ class Filter(object):
merged_filter = copy.deepcopy(self)
# Merge unique filter bins
merged_bins = set(np.concatenate((self.bins, other.bins)))
merged_bins = np.concatenate((self.bins, other.bins))
merged_bins = np.unique(merged_bins)
# Sort energy bin edges
if 'energy' in self.type:
@ -557,14 +576,8 @@ class Filter(object):
"""
# Attempt to import Pandas
try:
import pandas as pd
except ImportError:
msg = 'The Pandas Python package must be installed on your system'
raise ImportError(msg)
# Initialize Pandas DataFrame
import pandas as pd
df = pd.DataFrame()
# mesh filters
@ -743,7 +756,6 @@ class Filter(object):
filter_bins = np.repeat(filter_bins, self.stride)
tile_factor = data_size / len(filter_bins)
filter_bins = np.tile(filter_bins, tile_factor)
filter_bins = filter_bins
df = pd.DataFrame({self.type : filter_bins})
# If OpenCG level info DataFrame was created, concatenate

View file

@ -1368,7 +1368,6 @@ class MGXS(object):
# Sort the dataframe by domain type id (e.g., distribcell id) and
# energy groups such that data is from fast to thermal
df.sort([self.domain_type] + columns, inplace=True)
return df

View file

@ -301,7 +301,7 @@ class Tally(object):
@property
def sum(self):
if not self._sp_filename:
if not self._sp_filename or self.derived:
return None
if not self._results_read:
@ -924,6 +924,17 @@ class Tally(object):
if score not in merged_tally.scores:
merged_tally.add_score(score)
# Add triggers from other tally to merged tally
for trigger in other.triggers:
merged_tally.add_trigger(trigger)
# If results have not been read, then return tally for input generation
if self._sp_filename is None:
return merged_tally
#Otherwise, this is a derived tally which needs merged results arrays
else:
self._derived = True
# Update filter strides in merged tally
merged_tally._update_filter_strides()
@ -986,10 +997,6 @@ class Tally(object):
# Sparsify merged tally if both tallies are sparse
merged_tally.sparse = self.sparse and other.sparse
# Add triggers from other tally to merged tally
for trigger in other.triggers:
merged_tally.add_trigger(trigger)
return merged_tally
def get_tally_xml(self):
@ -1538,14 +1545,8 @@ class Tally(object):
'Summary info'.format(self.id)
raise KeyError(msg)
# Attempt to import Pandas
try:
import pandas as pd
except ImportError:
msg = 'The Pandas Python package must be installed on your system'
raise ImportError(msg)
# Initialize a pandas dataframe for the tally data
import pandas as pd
df = pd.DataFrame()
# Find the total length of the tally data array
@ -2189,8 +2190,8 @@ class Tally(object):
# 'since it does not contain any results.'.format(self.id)
# raise ValueError(msg)
cv.check_type('filter1', filter1, Filter)
cv.check_type('filter2', filter2, Filter)
cv.check_type('filter1', filter1, (Filter, CrossFilter, AggregateFilter))
cv.check_type('filter2', filter2, (Filter, CrossFilter, AggregateFilter))
# Check that the filters exist in the tally and are not the same
if filter1 == filter2:
@ -2923,6 +2924,7 @@ class Tally(object):
# Create deep copy of tally to return as sliced tally
new_tally = copy.deepcopy(self)
new_tally._derived = True
# Differentiate Tally with a new auto-generated Tally ID
new_tally.id = None
@ -3094,7 +3096,7 @@ class Tally(object):
# Add AggregateFilter to the tally sum
if not remove_filter:
filter_sum = \
AggregateFilter(self_filter, filter_bins, 'sum')
AggregateFilter(self_filter, [tuple(filter_bins)], 'sum')
tally_sum.add_filter(filter_sum)
# Add a copy of each filter not summed across to the tally sum
@ -3243,7 +3245,7 @@ class Tally(object):
# Add AggregateFilter to the tally avg
if not remove_filter:
filter_sum = \
AggregateFilter(self_filter, filter_bins, 'avg')
AggregateFilter(self_filter, [tuple(filter_bins)], 'avg')
tally_avg.add_filter(filter_sum)
# Add a copy of each filter not averaged across to the tally avg

View file

@ -1,5 +1,5 @@
sum(distribcell) group in nuclide mean std. dev.
0 sum(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, ... 1 total 0.720213 1.424323 sum(distribcell) group in nuclide mean std. dev.
0 sum(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, ... 1 total 0 0 sum(distribcell) group in group out nuclide mean std. dev.
0 sum(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, ... 1 1 total 0.70466 1.403916 sum(distribcell) group out nuclide mean std. dev.
0 sum(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, ... 1 total 0 0
0 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,... 1 total 0.720213 1.424323 sum(distribcell) group in nuclide mean std. dev.
0 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,... 1 total 0 0 sum(distribcell) group in group out nuclide mean std. dev.
0 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,... 1 1 total 0.70466 1.403916 sum(distribcell) group out nuclide mean std. dev.
0 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,... 1 total 0 0