Repository URL to install this package:
|
Version:
0.27.10 ▾
|
"""
Required Notice: Copyright (C) Zoomer Analytics GmbH.
xlwings PRO is dual-licensed under one of the following licenses:
* PolyForm Noncommercial License 1.0.0 (for noncommercial use):
https://polyformproject.org/licenses/noncommercial/1.0.0
* xlwings PRO License (for commercial use):
https://github.com/xlwings/xlwings/blob/main/LICENSE_PRO.txt
Commercial licenses can be purchased at https://www.xlwings.org
"""
import sys
try:
import numpy as np
except ImportError:
np = None
def _get_filter_value(filter_list, filter_name, default=None):
for f in filter_list:
for k, v in f.items():
if k == filter_name:
return v[0].as_const()
return default
# Standard Jinja custom filters
def datetime(value, format=None):
# Custom Jinja filter that can be used by strings/Markdown
if format is None:
# Default format: July 1, 2020
format = f"%B %{'#' if sys.platform.startswith('win') else '-'}d, %Y"
return value.strftime(format)
def fmt(value, format):
return f"{value:{format}}"
# Image filters
def width(filter_list):
return _get_filter_value(filter_list, "width")
def height(filter_list):
return _get_filter_value(filter_list, "height")
def scale(filter_list):
return _get_filter_value(filter_list, "scale")
def image_format(filter_list):
return _get_filter_value(filter_list, "format", "png")
def top(filter_list):
return _get_filter_value(filter_list, "top", 0)
def left(filter_list):
return _get_filter_value(filter_list, "left", 0)
# Font filters
def fontcolor(value=None, filter_list=None):
# Useful for working with white fonts without them disappearing on screen
if value:
# Ignore if called as standard Jinja filter
return value
elif filter_list:
# If called from a single cell/shape placeholder
color = _get_filter_value(filter_list, "fontcolor")
colors = {"white": "#ffffff", "black": "#000000"}
if color.lower() in colors:
return colors[color.lower()]
else:
return color
# DataFrame filters
def sortasc(df, filter_args):
if df.empty:
return df
columns = [arg.as_const() for arg in filter_args]
return df.sort_values(list(df.columns[columns]), ascending=True)
def sortdesc(df, filter_args):
if df.empty:
return df
columns = [arg.as_const() for arg in filter_args]
return df.sort_values(list(df.columns[columns]), ascending=False)
def mul(df, filter_args):
if df.empty:
return df
value, col_ix = filter_args[0].as_const(), filter_args[1].as_const()
fill_value = filter_args[2].as_const() if len(filter_args) > 2 else None
df.iloc[:, col_ix] = df.iloc[:, col_ix].mul(value, fill_value=fill_value)
return df
def div(df, filter_args):
if df.empty:
return df
value, col_ix = filter_args[0].as_const(), filter_args[1].as_const()
fill_value = filter_args[2].as_const() if len(filter_args) > 2 else None
df.iloc[:, col_ix] = df.iloc[:, col_ix].div(value, fill_value=fill_value)
return df
def add(df, filter_args):
if df.empty:
return df
value, col_ix = filter_args[0].as_const(), filter_args[1].as_const()
fill_value = filter_args[2].as_const() if len(filter_args) > 2 else None
df.iloc[:, col_ix] = df.iloc[:, col_ix].add(value, fill_value=fill_value)
return df
def sub(df, filter_args):
if df.empty:
return df
value, col_ix = filter_args[0].as_const(), filter_args[1].as_const()
fill_value = filter_args[2].as_const() if len(filter_args) > 2 else None
df.iloc[:, col_ix] = df.iloc[:, col_ix].sub(value, fill_value=fill_value)
return df
def maxrows(df, filter_args):
if df.empty:
return df
if len(df) > filter_args[0].as_const():
splitrow = filter_args[0].as_const() - 1
other = df.iloc[splitrow:, :].sum(numeric_only=True)
other_name = filter_args[1].as_const()
other.name = other_name
df = df.iloc[:splitrow, :].append(other)
col_ix = filter_args[2].as_const() if len(filter_args) > 2 else 0
df.iloc[-1, col_ix] = other_name
return df
def aggsmall(df, filter_args):
if df.empty:
return df
threshold = filter_args[0].as_const()
col_ix = filter_args[1].as_const()
other_name = filter_args[2].as_const()
other_ix = filter_args[3].as_const() if len(filter_args) > 3 else 0
min_rows = filter_args[4].as_const() if len(filter_args) > 4 else 1
df.loc[:, "__is_small__"] = df.iloc[:, col_ix] < threshold
if min_rows >= len(df):
df.loc[:, "__is_over_min__"] = False
else:
df.loc[:, "__is_over_min__"] = [False] * (min_rows - 1) + [True] * (
len(df) - min_rows + 1
)
df.loc[:, "__total__"] = df["__is_small__"] & df["__is_over_min__"]
if True in df["__total__"].unique():
# unlike aggregate, groupby conveniently drops non-numeric values
other = df.groupby("__total__").sum().loc[True, :]
other.name = other_name
df = df.loc[~df["__total__"], :].append(other)
df.iloc[-1, other_ix] = other_name
df = df.drop(columns=["__is_small__", "__is_over_min__", "__total__"])
return df
def head(df, filter_args):
if df.empty:
return df
return df.head(filter_args[0].as_const())
def tail(df, filter_args):
if df.empty:
return df
return df.tail(filter_args[0].as_const())
def rowslice(df, filter_args):
if df.empty:
return df
args = [arg.as_const() for arg in filter_args]
if len(args) == 1:
args.append(None)
df = df.iloc[args[0] : args[1], :]
return df
def colslice(df, filter_args):
args = [arg.as_const() for arg in filter_args]
if len(args) == 1:
args.append(None)
df = df.iloc[:, args[0] : args[1]]
return df
def columns(df, filter_args):
if df.empty:
return df
columns = [arg.as_const() for arg in filter_args]
df = df.iloc[:, [col for col in columns if col is not None]]
empty_col_indices = [i for i, v in enumerate(columns) if v is None]
for n, col_ix in enumerate(empty_col_indices):
# insert() method is inplace!
# Since Excel tables only allow an empty space once, we'll generate multiple
# empty spaces for each column.
df.insert(loc=col_ix, column=" " * (n + 1), value=np.nan)
return df
def header(df, filter_args):
# Replace the spaces introduced by a potential previous call of columns()
# as headers alone can't be used in Excel tables
return [None if i.isspace() else i for i in df.columns]