openpyxl
openpyxl copied to clipboard
Add fill_foward to headers
Hi!, i dont know how to add this code on GitHub, so hope some can add this
Code original:
def dataframe_to_rows(df, index=True, header=True):
"""
Convert a Pandas dataframe into something suitable for passing into a worksheet.
If index is True then the index will be included, starting one row below the header.
If header is True then column headers will be included starting one column to the right.
Formatting should be done by client code.
"""
from pandas import Timestamp
if header:
if df.columns.nlevels > 1:
rows = expand_index(df.columns, header)
else:
rows = [list(df.columns.values)]
for row in rows:
n = []
for v in row:
if isinstance(v, numpy.datetime64):
v = Timestamp(v)
n.append(v)
row = n
if index:
row = [None]*df.index.nlevels + row
yield row
if index:
yield df.index.names
expanded = ([v] for v in df.index)
if df.index.nlevels > 1:
expanded = expand_index(df.index)
# Using the expanded index is preferable to df.itertuples(index=True) so that we have 'None' inserted where applicable
for (df_index, row) in zip(expanded, df.itertuples(index=False)):
row = list(row)
if index:
row = df_index + row
yield row
Adding:
def fill_nones(data):
"""
Fills in None values in a list with the preceding non-None value.
Args:
data (list): The list containing data, possibly with None values.
Returns:
list: The list with None values filled in.
"""
previous_value = None
for i in range(len(data)):
if data[i] is None:
data[i] = previous_value
else:
previous_value = data[i]
return data
def dataframe_to_rows(df, index=True, header=True, fill_foward=False):
"""
Convert a Pandas dataframe into something suitable for passing into a worksheet.
If index is True then the index will be included, starting one row below the header.
If header is True then column headers will be included starting one column to the right.
Formatting should be done by client code.
"""
from pandas import Timestamp
if header:
if df.columns.nlevels > 1:
rows = expand_index(df.columns, header)
else:
rows = [list(df.columns.values)]
for row in rows:
if fill_foward: # HERE IS WHERE PUT THE CONDITION
row = fill_nones(row)
n = []
for v in row:
if isinstance(v, numpy.datetime64):
v = Timestamp(v)
n.append(v)
row = n
if index:
row = [None]*df.index.nlevels + row
yield row
if index:
yield df.index.names
expanded = ([v] for v in df.index)
if df.index.nlevels > 1:
expanded = expand_index(df.index)
# Using the expanded index is preferable to df.itertuples(index=True) so that we have 'None' inserted where applicable
for (df_index, row) in zip(expanded, df.itertuples(index=False)):
row = list(row)
if index:
row = df_index + row
yield row
Why? This helps when you use pandas with multiindex, writting using
for r in dataframe_to_rows(
df, index=False, header=True, fill_foward=True
):
ws.append(r)
This keep the "merge" on the header/multiindex, instead a None values.