Skip to content

Commit d7b11e9

Browse files
authored
Merge pull request #67 from ichrys03/to_excelfix
to_excel update for ids #118 from dt
2 parents ed63f57 + 32382c5 commit d7b11e9

File tree

2 files changed

+151
-60
lines changed

2 files changed

+151
-60
lines changed

epyt/epanet.py

Lines changed: 132 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -384,24 +384,46 @@ def to_dict(self):
384384
dict_values = vars(self)
385385
return dict_values
386386

387-
def to_excel(self, filename=None, attributes=None, allValues=False):
388-
""" Save to an Excel file the values of EpytValues class
387+
def to_excel(self, filename=None, attributes=None, allValues=False,
388+
node_id_list=None, link_id_list=None, both=False, header=True):
389+
"""
390+
Save to an Excel file the values of EpytValues class.
389391

390-
:param self: Values to add to the Excel file
391-
:type self: EpytValues class
392-
:param filename: excel filename, defaults to None
392+
:param filename: Excel filename, defaults to None
393393
:type filename: str, optional
394-
:param attributes: attributes to add to the file, defaults to None
394+
:param attributes: Attributes to add to the file, defaults to None
395395
:type attributes: str or list of str, optional
396-
:param allValues: 'True' if all the values will be included in a
397-
separate sheet, defaults to False
396+
:param allValues: If True, writes all the values into a separate "All values" sheet, defaults to False
398397
:type allValues: bool, optional
398+
:param node_id_list: Array of IDs for node-related attributes
399+
:type node_id_list: list or np.ndarray, optional
400+
:param link_id_list: Array of IDs for link-related attributes
401+
:type link_id_list: list or np.ndarray, optional
402+
:param both: If True, and ID array available, print both 'Index' and 'Id'. If no ID array, just Index.
403+
If False and ID array available, print only 'Id'; if no ID array, print only 'Index'.
404+
:type both: bool, optional
405+
:param header: If False, remove the first row from all sheets and do not write column headers
406+
:type header: bool, optional
399407
:return: None
400-
401408
"""
409+
node_keywords = ['nodequality', 'head', 'demand', 'pressure']
410+
link_keywords = [
411+
'linkquality', 'flow', 'velocity', 'headloss',
412+
'Status', 'Setting', 'ReactionRate', 'StatusStr', 'FrictionFactor'
413+
]
414+
415+
def is_node_attribute(key):
416+
key_lower = key.lower()
417+
return (any(re.search(r'\b' + kw + r'\b', key_lower) for kw in node_keywords)
418+
or 'node' in key_lower)
419+
420+
def is_link_attribute(key):
421+
key_lower = key.lower()
422+
return (any(re.search(r'\b' + kw + r'\b', key_lower) for kw in link_keywords)
423+
or 'link' in key_lower)
424+
402425
if filename is None:
403-
rand_id = ''.join(random.choices(string.ascii_letters
404-
+ string.digits, k=5))
426+
rand_id = ''.join(random.choices(string.ascii_letters + string.digits, k=5))
405427
filename = 'ToExcelfile_' + rand_id + '.xlsx'
406428
if '.xlsx' not in filename:
407429
filename = filename + '.xlsx'
@@ -414,58 +436,108 @@ def to_excel(self, filename=None, attributes=None, allValues=False):
414436
else:
415437
dictValss[i] = dictVals[i]
416438
dictVals = dictValss
439+
440+
def process_dataframe(key, df):
441+
header_labels = []
442+
num_rows = len(df)
443+
chosen_id_array = None
444+
445+
if is_node_attribute(key) and node_id_list is not None:
446+
chosen_id_array = node_id_list
447+
elif is_link_attribute(key) and link_id_list is not None:
448+
chosen_id_array = link_id_list
449+
450+
print_id_col = False
451+
print_index_col = False
452+
453+
if both:
454+
print_index_col = True
455+
if chosen_id_array is not None:
456+
print_id_col = True
457+
else:
458+
if chosen_id_array is not None:
459+
print_id_col = True
460+
else:
461+
print_index_col = True
462+
463+
if print_id_col and chosen_id_array is not None:
464+
if len(chosen_id_array) != num_rows:
465+
warnings.warn(
466+
f"ID array length does not match rows for '{key}'. Truncating."
467+
)
468+
adjusted_id_array = chosen_id_array[:num_rows]
469+
df.insert(0, 'Id', adjusted_id_array)
470+
header_labels.append('Id')
471+
472+
if print_index_col:
473+
index_values = list(range(1, num_rows + 1))
474+
if print_id_col:
475+
id_col = df.pop('Id')
476+
df.insert(0, 'Index', index_values)
477+
df.insert(1, 'Id', id_col)
478+
header_labels.insert(0, 'Index')
479+
else:
480+
df.insert(0, "Index", index_values)
481+
header_labels.insert(0, 'Index')
482+
483+
num_data_columns = df.shape[1] - len(header_labels)
484+
if 'Time' in dictVals and len(dictVals['Time']) == num_data_columns:
485+
header_labels.extend(dictVals['Time'])
486+
else:
487+
data_column_names = [f"Column{i + 1}" for i in range(num_data_columns)]
488+
header_labels.extend(data_column_names)
489+
490+
df.columns = header_labels
491+
return df
492+
417493
with pd.ExcelWriter(filename, mode="w") as writer:
418-
for key in dictVals:
419-
if 'Time' not in key:
420-
if not attributes:
421-
df = pd.DataFrame(dictVals[key])
422-
df.insert(0, "Index", list(range(1, len(dictVals[key]) + 1)), True)
423-
df.set_index("Index", inplace=True)
424-
df.to_excel(writer, sheet_name=key,
425-
header=dictVals['Time'])
426-
else:
427-
if not isList(attributes):
428-
attributes = [attributes]
429-
if key in attributes:
430-
df = pd.DataFrame(dictVals[key])
431-
df.insert(0, "Index", list(range(1, len(dictVals[key]) + 1)), True)
432-
df.set_index("Index", inplace=True)
433-
df.to_excel(writer,
434-
sheet_name=key,
435-
header=dictVals['Time'])
494+
495+
for key, data in dictVals.items():
496+
if key == 'Time':
497+
continue
498+
if attributes and key not in attributes:
499+
continue
500+
if not isinstance(data, (list, np.ndarray, pd.Series, pd.DataFrame)):
501+
continue
502+
503+
df = pd.DataFrame(data)
504+
df = process_dataframe(key, df)
505+
506+
if not header:
507+
df.columns = df.iloc[0]
508+
df = df.iloc[1:]
509+
df.to_excel(writer, sheet_name=key, index=False, header=True)
510+
else:
511+
df.to_excel(writer, sheet_name=key, index=False, header=True)
512+
436513
if allValues:
514+
worksheet_name = 'All values'
437515
first_iter = True
438-
titleFormat = writer.book.add_format(
439-
{'bold': True, 'align': 'center',
440-
'valign': 'vcenter', 'font_size': 16})
441-
for key in dictVals:
442-
if key != 'Time' and not attributes:
443-
df = pd.DataFrame(dictVals[key])
444-
df.insert(0, "Index", list(range(1, len(dictVals[key]) + 1)), True)
445-
df.set_index("Index", inplace=True)
446-
if first_iter:
447-
df.to_excel(
448-
writer,
449-
sheet_name='All values',
450-
header=dictVals['Time'],
451-
startrow=1
452-
)
453-
writer.book.worksheets()[-1].write(0, 1, key,
454-
titleFormat)
455-
first_iter = False
456-
else:
457-
startrow = writer.book.worksheets()[-1].dim_rowmax \
458-
+ 3
459-
writer.book.worksheets()[-1].write(startrow - 1,
460-
1,
461-
key,
462-
titleFormat)
463-
df.to_excel(
464-
writer,
465-
sheet_name='All values',
466-
header=dictVals['Time'],
467-
startrow=startrow
468-
)
516+
for key, data in dictVals.items():
517+
if key == 'Time':
518+
continue
519+
if attributes and key not in attributes:
520+
continue
521+
if not isinstance(data, (list, np.ndarray, pd.Series, pd.DataFrame)):
522+
print(f"Skipping key '{key}' due to unsupported data type: {type(data)}")
523+
continue
524+
525+
df = pd.DataFrame(data)
526+
df = process_dataframe(key, df)
527+
528+
529+
worksheet = writer.sheets.get(worksheet_name)
530+
if first_iter:
531+
df.to_excel(writer, sheet_name=worksheet_name, index=False, header=header, startrow=1)
532+
worksheet = writer.sheets[worksheet_name]
533+
worksheet.write(0, 0, key)
534+
first_iter = False
535+
else:
536+
startrow = worksheet.dim_rowmax + 3
537+
worksheet.write(startrow - 1, 0, key)
538+
df.to_excel(writer, sheet_name=worksheet_name, index=False, header=header, startrow=startrow)
539+
540+
print(f"Data successfully exported to {filename}")
469541

470542
def to_json(self, filename=None):
471543
""" Transforms val class values to json object and saves them

epyt/examples/python/EX_to_excel_json.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,5 +57,24 @@
5757
comp_values.to_excel(f"to_excel_{selected_attribute}_example",
5858
attributes=selected_attribute)
5959

60+
# Retrieve node and link IDs from the network
61+
nodeid = d.getNodeNameID()
62+
linkid = d.getLinkNameID()
63+
64+
# Below are four example scenarios demonstrating how to use comp_values.to_excel()
65+
# with different parameter configurations:
66+
67+
# 1) Scenario 1: Include both index and node/link IDs in the output.
68+
comp_values.to_excel("case1", node_id_list=nodeid, link_id_list=linkid, both=True)
69+
70+
# 2) Scenario 2: Include only node/link IDs (no index).
71+
comp_values.to_excel("case2", node_id_list=nodeid, link_id_list=linkid, both=False)
72+
73+
# 3) Scenario 3: Use the default settings (only index is included).
74+
comp_values.to_excel("case3")
75+
76+
# 4) Scenario 4: Use the default settings but suppress the column headers.
77+
comp_values.to_excel("case4", header=False)
78+
6079
# Unload library
6180
d.unload()

0 commit comments

Comments
 (0)