8th day of python challenges 111-117
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
"""
|
||||
Internal module for console introspection
|
||||
"""
|
||||
|
||||
from shutil import get_terminal_size
|
||||
|
||||
|
||||
def get_console_size():
|
||||
"""Return console size as tuple = (width, height).
|
||||
|
||||
Returns (None,None) in non-interactive session.
|
||||
"""
|
||||
from pandas import get_option
|
||||
|
||||
display_width = get_option("display.width")
|
||||
# deprecated.
|
||||
display_height = get_option("display.max_rows")
|
||||
|
||||
# Consider
|
||||
# interactive shell terminal, can detect term size
|
||||
# interactive non-shell terminal (ipnb/ipqtconsole), cannot detect term
|
||||
# size non-interactive script, should disregard term size
|
||||
|
||||
# in addition
|
||||
# width,height have default values, but setting to 'None' signals
|
||||
# should use Auto-Detection, But only in interactive shell-terminal.
|
||||
# Simple. yeah.
|
||||
|
||||
if in_interactive_session():
|
||||
if in_ipython_frontend():
|
||||
# sane defaults for interactive non-shell terminal
|
||||
# match default for width,height in config_init
|
||||
from pandas._config.config import get_default_val
|
||||
|
||||
terminal_width = get_default_val("display.width")
|
||||
terminal_height = get_default_val("display.max_rows")
|
||||
else:
|
||||
# pure terminal
|
||||
terminal_width, terminal_height = get_terminal_size()
|
||||
else:
|
||||
terminal_width, terminal_height = None, None
|
||||
|
||||
# Note if the User sets width/Height to None (auto-detection)
|
||||
# and we're in a script (non-inter), this will return (None,None)
|
||||
# caller needs to deal.
|
||||
return (display_width or terminal_width, display_height or terminal_height)
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Detect our environment
|
||||
|
||||
|
||||
def in_interactive_session():
|
||||
""" check if we're running in an interactive shell
|
||||
|
||||
returns True if running under python/ipython interactive shell
|
||||
"""
|
||||
from pandas import get_option
|
||||
|
||||
def check_main():
|
||||
try:
|
||||
import __main__ as main
|
||||
except ModuleNotFoundError:
|
||||
return get_option("mode.sim_interactive")
|
||||
return not hasattr(main, "__file__") or get_option("mode.sim_interactive")
|
||||
|
||||
try:
|
||||
return __IPYTHON__ or check_main() # noqa
|
||||
except NameError:
|
||||
return check_main()
|
||||
|
||||
|
||||
def in_ipython_frontend():
|
||||
"""
|
||||
check if we're inside an an IPython zmq frontend
|
||||
"""
|
||||
try:
|
||||
ip = get_ipython() # noqa
|
||||
return "zmq" in str(type(ip)).lower()
|
||||
except NameError:
|
||||
pass
|
||||
|
||||
return False
|
257
venv/lib/python3.6/site-packages/pandas/io/formats/css.py
Normal file
257
venv/lib/python3.6/site-packages/pandas/io/formats/css.py
Normal file
@@ -0,0 +1,257 @@
|
||||
"""Utilities for interpreting CSS from Stylers for formatting non-HTML outputs
|
||||
"""
|
||||
|
||||
import re
|
||||
import warnings
|
||||
|
||||
|
||||
class CSSWarning(UserWarning):
|
||||
"""This CSS syntax cannot currently be parsed"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class CSSResolver:
|
||||
"""A callable for parsing and resolving CSS to atomic properties
|
||||
|
||||
"""
|
||||
|
||||
def __call__(self, declarations_str, inherited=None):
|
||||
""" the given declarations to atomic properties
|
||||
|
||||
Parameters
|
||||
----------
|
||||
declarations_str : str
|
||||
A list of CSS declarations
|
||||
inherited : dict, optional
|
||||
Atomic properties indicating the inherited style context in which
|
||||
declarations_str is to be resolved. ``inherited`` should already
|
||||
be resolved, i.e. valid output of this method.
|
||||
|
||||
Returns
|
||||
-------
|
||||
props : dict
|
||||
Atomic CSS 2.2 properties
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> resolve = CSSResolver()
|
||||
>>> inherited = {'font-family': 'serif', 'font-weight': 'bold'}
|
||||
>>> out = resolve('''
|
||||
... border-color: BLUE RED;
|
||||
... font-size: 1em;
|
||||
... font-size: 2em;
|
||||
... font-weight: normal;
|
||||
... font-weight: inherit;
|
||||
... ''', inherited)
|
||||
>>> sorted(out.items()) # doctest: +NORMALIZE_WHITESPACE
|
||||
[('border-bottom-color', 'blue'),
|
||||
('border-left-color', 'red'),
|
||||
('border-right-color', 'red'),
|
||||
('border-top-color', 'blue'),
|
||||
('font-family', 'serif'),
|
||||
('font-size', '24pt'),
|
||||
('font-weight', 'bold')]
|
||||
"""
|
||||
|
||||
props = dict(self.atomize(self.parse(declarations_str)))
|
||||
if inherited is None:
|
||||
inherited = {}
|
||||
|
||||
# 1. resolve inherited, initial
|
||||
for prop, val in inherited.items():
|
||||
if prop not in props:
|
||||
props[prop] = val
|
||||
|
||||
for prop, val in list(props.items()):
|
||||
if val == "inherit":
|
||||
val = inherited.get(prop, "initial")
|
||||
if val == "initial":
|
||||
val = None
|
||||
|
||||
if val is None:
|
||||
# we do not define a complete initial stylesheet
|
||||
del props[prop]
|
||||
else:
|
||||
props[prop] = val
|
||||
|
||||
# 2. resolve relative font size
|
||||
if props.get("font-size"):
|
||||
if "font-size" in inherited:
|
||||
em_pt = inherited["font-size"]
|
||||
assert em_pt[-2:] == "pt"
|
||||
em_pt = float(em_pt[:-2])
|
||||
else:
|
||||
em_pt = None
|
||||
props["font-size"] = self.size_to_pt(
|
||||
props["font-size"], em_pt, conversions=self.FONT_SIZE_RATIOS
|
||||
)
|
||||
|
||||
font_size = float(props["font-size"][:-2])
|
||||
else:
|
||||
font_size = None
|
||||
|
||||
# 3. TODO: resolve other font-relative units
|
||||
for side in self.SIDES:
|
||||
prop = "border-{side}-width".format(side=side)
|
||||
if prop in props:
|
||||
props[prop] = self.size_to_pt(
|
||||
props[prop], em_pt=font_size, conversions=self.BORDER_WIDTH_RATIOS
|
||||
)
|
||||
for prop in [
|
||||
"margin-{side}".format(side=side),
|
||||
"padding-{side}".format(side=side),
|
||||
]:
|
||||
if prop in props:
|
||||
# TODO: support %
|
||||
props[prop] = self.size_to_pt(
|
||||
props[prop], em_pt=font_size, conversions=self.MARGIN_RATIOS
|
||||
)
|
||||
|
||||
return props
|
||||
|
||||
UNIT_RATIOS = {
|
||||
"rem": ("pt", 12),
|
||||
"ex": ("em", 0.5),
|
||||
# 'ch':
|
||||
"px": ("pt", 0.75),
|
||||
"pc": ("pt", 12),
|
||||
"in": ("pt", 72),
|
||||
"cm": ("in", 1 / 2.54),
|
||||
"mm": ("in", 1 / 25.4),
|
||||
"q": ("mm", 0.25),
|
||||
"!!default": ("em", 0),
|
||||
}
|
||||
|
||||
FONT_SIZE_RATIOS = UNIT_RATIOS.copy()
|
||||
FONT_SIZE_RATIOS.update(
|
||||
{
|
||||
"%": ("em", 0.01),
|
||||
"xx-small": ("rem", 0.5),
|
||||
"x-small": ("rem", 0.625),
|
||||
"small": ("rem", 0.8),
|
||||
"medium": ("rem", 1),
|
||||
"large": ("rem", 1.125),
|
||||
"x-large": ("rem", 1.5),
|
||||
"xx-large": ("rem", 2),
|
||||
"smaller": ("em", 1 / 1.2),
|
||||
"larger": ("em", 1.2),
|
||||
"!!default": ("em", 1),
|
||||
}
|
||||
)
|
||||
|
||||
MARGIN_RATIOS = UNIT_RATIOS.copy()
|
||||
MARGIN_RATIOS.update({"none": ("pt", 0)})
|
||||
|
||||
BORDER_WIDTH_RATIOS = UNIT_RATIOS.copy()
|
||||
BORDER_WIDTH_RATIOS.update(
|
||||
{
|
||||
"none": ("pt", 0),
|
||||
"thick": ("px", 4),
|
||||
"medium": ("px", 2),
|
||||
"thin": ("px", 1),
|
||||
# Default: medium only if solid
|
||||
}
|
||||
)
|
||||
|
||||
def size_to_pt(self, in_val, em_pt=None, conversions=UNIT_RATIOS):
|
||||
def _error():
|
||||
warnings.warn("Unhandled size: {val!r}".format(val=in_val), CSSWarning)
|
||||
return self.size_to_pt("1!!default", conversions=conversions)
|
||||
|
||||
try:
|
||||
val, unit = re.match(r"^(\S*?)([a-zA-Z%!].*)", in_val).groups()
|
||||
except AttributeError:
|
||||
return _error()
|
||||
if val == "":
|
||||
# hack for 'large' etc.
|
||||
val = 1
|
||||
else:
|
||||
try:
|
||||
val = float(val)
|
||||
except ValueError:
|
||||
return _error()
|
||||
|
||||
while unit != "pt":
|
||||
if unit == "em":
|
||||
if em_pt is None:
|
||||
unit = "rem"
|
||||
else:
|
||||
val *= em_pt
|
||||
unit = "pt"
|
||||
continue
|
||||
|
||||
try:
|
||||
unit, mul = conversions[unit]
|
||||
except KeyError:
|
||||
return _error()
|
||||
val *= mul
|
||||
|
||||
val = round(val, 5)
|
||||
if int(val) == val:
|
||||
size_fmt = "{fmt:d}pt".format(fmt=int(val))
|
||||
else:
|
||||
size_fmt = "{fmt:f}pt".format(fmt=val)
|
||||
return size_fmt
|
||||
|
||||
def atomize(self, declarations):
|
||||
for prop, value in declarations:
|
||||
attr = "expand_" + prop.replace("-", "_")
|
||||
try:
|
||||
expand = getattr(self, attr)
|
||||
except AttributeError:
|
||||
yield prop, value
|
||||
else:
|
||||
for prop, value in expand(prop, value):
|
||||
yield prop, value
|
||||
|
||||
SIDE_SHORTHANDS = {
|
||||
1: [0, 0, 0, 0],
|
||||
2: [0, 1, 0, 1],
|
||||
3: [0, 1, 2, 1],
|
||||
4: [0, 1, 2, 3],
|
||||
}
|
||||
SIDES = ("top", "right", "bottom", "left")
|
||||
|
||||
def _side_expander(prop_fmt):
|
||||
def expand(self, prop, value):
|
||||
tokens = value.split()
|
||||
try:
|
||||
mapping = self.SIDE_SHORTHANDS[len(tokens)]
|
||||
except KeyError:
|
||||
warnings.warn(
|
||||
'Could not expand "{prop}: {val}"'.format(prop=prop, val=value),
|
||||
CSSWarning,
|
||||
)
|
||||
return
|
||||
for key, idx in zip(self.SIDES, mapping):
|
||||
yield prop_fmt.format(key), tokens[idx]
|
||||
|
||||
return expand
|
||||
|
||||
expand_border_color = _side_expander("border-{:s}-color")
|
||||
expand_border_style = _side_expander("border-{:s}-style")
|
||||
expand_border_width = _side_expander("border-{:s}-width")
|
||||
expand_margin = _side_expander("margin-{:s}")
|
||||
expand_padding = _side_expander("padding-{:s}")
|
||||
|
||||
def parse(self, declarations_str):
|
||||
"""Generates (prop, value) pairs from declarations
|
||||
|
||||
In a future version may generate parsed tokens from tinycss/tinycss2
|
||||
"""
|
||||
for decl in declarations_str.split(";"):
|
||||
if not decl.strip():
|
||||
continue
|
||||
prop, sep, val = decl.partition(":")
|
||||
prop = prop.strip().lower()
|
||||
# TODO: don't lowercase case sensitive parts of values (strings)
|
||||
val = val.strip().lower()
|
||||
if sep:
|
||||
yield prop, val
|
||||
else:
|
||||
warnings.warn(
|
||||
"Ill-formatted attribute: expected a colon "
|
||||
"in {decl!r}".format(decl=decl),
|
||||
CSSWarning,
|
||||
)
|
356
venv/lib/python3.6/site-packages/pandas/io/formats/csvs.py
Normal file
356
venv/lib/python3.6/site-packages/pandas/io/formats/csvs.py
Normal file
@@ -0,0 +1,356 @@
|
||||
"""
|
||||
Module for formatting output data into CSV files.
|
||||
"""
|
||||
|
||||
import csv as csvlib
|
||||
from io import StringIO
|
||||
import os
|
||||
import warnings
|
||||
from zipfile import ZipFile
|
||||
|
||||
import numpy as np
|
||||
|
||||
from pandas._libs import writers as libwriters
|
||||
|
||||
from pandas.core.dtypes.generic import (
|
||||
ABCDatetimeIndex,
|
||||
ABCIndexClass,
|
||||
ABCMultiIndex,
|
||||
ABCPeriodIndex,
|
||||
)
|
||||
from pandas.core.dtypes.missing import notna
|
||||
|
||||
from pandas.io.common import (
|
||||
UnicodeWriter,
|
||||
_get_handle,
|
||||
_infer_compression,
|
||||
get_filepath_or_buffer,
|
||||
)
|
||||
|
||||
|
||||
class CSVFormatter:
|
||||
def __init__(
|
||||
self,
|
||||
obj,
|
||||
path_or_buf=None,
|
||||
sep=",",
|
||||
na_rep="",
|
||||
float_format=None,
|
||||
cols=None,
|
||||
header=True,
|
||||
index=True,
|
||||
index_label=None,
|
||||
mode="w",
|
||||
encoding=None,
|
||||
compression="infer",
|
||||
quoting=None,
|
||||
line_terminator="\n",
|
||||
chunksize=None,
|
||||
quotechar='"',
|
||||
date_format=None,
|
||||
doublequote=True,
|
||||
escapechar=None,
|
||||
decimal=".",
|
||||
):
|
||||
|
||||
self.obj = obj
|
||||
|
||||
if path_or_buf is None:
|
||||
path_or_buf = StringIO()
|
||||
|
||||
self.path_or_buf, _, _, _ = get_filepath_or_buffer(
|
||||
path_or_buf, encoding=encoding, compression=compression, mode=mode
|
||||
)
|
||||
self.sep = sep
|
||||
self.na_rep = na_rep
|
||||
self.float_format = float_format
|
||||
self.decimal = decimal
|
||||
|
||||
self.header = header
|
||||
self.index = index
|
||||
self.index_label = index_label
|
||||
self.mode = mode
|
||||
if encoding is None:
|
||||
encoding = "utf-8"
|
||||
self.encoding = encoding
|
||||
self.compression = _infer_compression(self.path_or_buf, compression)
|
||||
|
||||
if quoting is None:
|
||||
quoting = csvlib.QUOTE_MINIMAL
|
||||
self.quoting = quoting
|
||||
|
||||
if quoting == csvlib.QUOTE_NONE:
|
||||
# prevents crash in _csv
|
||||
quotechar = None
|
||||
self.quotechar = quotechar
|
||||
|
||||
self.doublequote = doublequote
|
||||
self.escapechar = escapechar
|
||||
|
||||
self.line_terminator = line_terminator or os.linesep
|
||||
|
||||
self.date_format = date_format
|
||||
|
||||
self.has_mi_columns = isinstance(obj.columns, ABCMultiIndex)
|
||||
|
||||
# validate mi options
|
||||
if self.has_mi_columns:
|
||||
if cols is not None:
|
||||
raise TypeError(
|
||||
"cannot specify cols with a MultiIndex on the " "columns"
|
||||
)
|
||||
|
||||
if cols is not None:
|
||||
if isinstance(cols, ABCIndexClass):
|
||||
cols = cols.to_native_types(
|
||||
na_rep=na_rep,
|
||||
float_format=float_format,
|
||||
date_format=date_format,
|
||||
quoting=self.quoting,
|
||||
)
|
||||
else:
|
||||
cols = list(cols)
|
||||
self.obj = self.obj.loc[:, cols]
|
||||
|
||||
# update columns to include possible multiplicity of dupes
|
||||
# and make sure sure cols is just a list of labels
|
||||
cols = self.obj.columns
|
||||
if isinstance(cols, ABCIndexClass):
|
||||
cols = cols.to_native_types(
|
||||
na_rep=na_rep,
|
||||
float_format=float_format,
|
||||
date_format=date_format,
|
||||
quoting=self.quoting,
|
||||
)
|
||||
else:
|
||||
cols = list(cols)
|
||||
|
||||
# save it
|
||||
self.cols = cols
|
||||
|
||||
# preallocate data 2d list
|
||||
self.blocks = self.obj._data.blocks
|
||||
ncols = sum(b.shape[0] for b in self.blocks)
|
||||
self.data = [None] * ncols
|
||||
|
||||
if chunksize is None:
|
||||
chunksize = (100000 // (len(self.cols) or 1)) or 1
|
||||
self.chunksize = int(chunksize)
|
||||
|
||||
self.data_index = obj.index
|
||||
if (
|
||||
isinstance(self.data_index, (ABCDatetimeIndex, ABCPeriodIndex))
|
||||
and date_format is not None
|
||||
):
|
||||
from pandas import Index
|
||||
|
||||
self.data_index = Index(
|
||||
[x.strftime(date_format) if notna(x) else "" for x in self.data_index]
|
||||
)
|
||||
|
||||
self.nlevels = getattr(self.data_index, "nlevels", 1)
|
||||
if not index:
|
||||
self.nlevels = 0
|
||||
|
||||
def save(self):
|
||||
"""
|
||||
Create the writer & save
|
||||
"""
|
||||
# GH21227 internal compression is not used when file-like passed.
|
||||
if self.compression and hasattr(self.path_or_buf, "write"):
|
||||
msg = "compression has no effect when passing file-like " "object as input."
|
||||
warnings.warn(msg, RuntimeWarning, stacklevel=2)
|
||||
|
||||
# when zip compression is called.
|
||||
is_zip = isinstance(self.path_or_buf, ZipFile) or (
|
||||
not hasattr(self.path_or_buf, "write") and self.compression == "zip"
|
||||
)
|
||||
|
||||
if is_zip:
|
||||
# zipfile doesn't support writing string to archive. uses string
|
||||
# buffer to receive csv writing and dump into zip compression
|
||||
# file handle. GH21241, GH21118
|
||||
f = StringIO()
|
||||
close = False
|
||||
elif hasattr(self.path_or_buf, "write"):
|
||||
f = self.path_or_buf
|
||||
close = False
|
||||
else:
|
||||
f, handles = _get_handle(
|
||||
self.path_or_buf,
|
||||
self.mode,
|
||||
encoding=self.encoding,
|
||||
compression=self.compression,
|
||||
)
|
||||
close = True
|
||||
|
||||
try:
|
||||
writer_kwargs = dict(
|
||||
lineterminator=self.line_terminator,
|
||||
delimiter=self.sep,
|
||||
quoting=self.quoting,
|
||||
doublequote=self.doublequote,
|
||||
escapechar=self.escapechar,
|
||||
quotechar=self.quotechar,
|
||||
)
|
||||
if self.encoding == "ascii":
|
||||
self.writer = csvlib.writer(f, **writer_kwargs)
|
||||
else:
|
||||
writer_kwargs["encoding"] = self.encoding
|
||||
self.writer = UnicodeWriter(f, **writer_kwargs)
|
||||
|
||||
self._save()
|
||||
|
||||
finally:
|
||||
if is_zip:
|
||||
# GH17778 handles zip compression separately.
|
||||
buf = f.getvalue()
|
||||
if hasattr(self.path_or_buf, "write"):
|
||||
self.path_or_buf.write(buf)
|
||||
else:
|
||||
f, handles = _get_handle(
|
||||
self.path_or_buf,
|
||||
self.mode,
|
||||
encoding=self.encoding,
|
||||
compression=self.compression,
|
||||
)
|
||||
f.write(buf)
|
||||
close = True
|
||||
if close:
|
||||
f.close()
|
||||
for _fh in handles:
|
||||
_fh.close()
|
||||
|
||||
def _save_header(self):
|
||||
|
||||
writer = self.writer
|
||||
obj = self.obj
|
||||
index_label = self.index_label
|
||||
cols = self.cols
|
||||
has_mi_columns = self.has_mi_columns
|
||||
header = self.header
|
||||
encoded_labels = []
|
||||
|
||||
has_aliases = isinstance(header, (tuple, list, np.ndarray, ABCIndexClass))
|
||||
if not (has_aliases or self.header):
|
||||
return
|
||||
if has_aliases:
|
||||
if len(header) != len(cols):
|
||||
raise ValueError(
|
||||
(
|
||||
"Writing {ncols} cols but got {nalias} "
|
||||
"aliases".format(ncols=len(cols), nalias=len(header))
|
||||
)
|
||||
)
|
||||
else:
|
||||
write_cols = header
|
||||
else:
|
||||
write_cols = cols
|
||||
|
||||
if self.index:
|
||||
# should write something for index label
|
||||
if index_label is not False:
|
||||
if index_label is None:
|
||||
if isinstance(obj.index, ABCMultiIndex):
|
||||
index_label = []
|
||||
for i, name in enumerate(obj.index.names):
|
||||
if name is None:
|
||||
name = ""
|
||||
index_label.append(name)
|
||||
else:
|
||||
index_label = obj.index.name
|
||||
if index_label is None:
|
||||
index_label = [""]
|
||||
else:
|
||||
index_label = [index_label]
|
||||
elif not isinstance(
|
||||
index_label, (list, tuple, np.ndarray, ABCIndexClass)
|
||||
):
|
||||
# given a string for a DF with Index
|
||||
index_label = [index_label]
|
||||
|
||||
encoded_labels = list(index_label)
|
||||
else:
|
||||
encoded_labels = []
|
||||
|
||||
if not has_mi_columns or has_aliases:
|
||||
encoded_labels += list(write_cols)
|
||||
writer.writerow(encoded_labels)
|
||||
else:
|
||||
# write out the mi
|
||||
columns = obj.columns
|
||||
|
||||
# write out the names for each level, then ALL of the values for
|
||||
# each level
|
||||
for i in range(columns.nlevels):
|
||||
|
||||
# we need at least 1 index column to write our col names
|
||||
col_line = []
|
||||
if self.index:
|
||||
|
||||
# name is the first column
|
||||
col_line.append(columns.names[i])
|
||||
|
||||
if isinstance(index_label, list) and len(index_label) > 1:
|
||||
col_line.extend([""] * (len(index_label) - 1))
|
||||
|
||||
col_line.extend(columns._get_level_values(i))
|
||||
|
||||
writer.writerow(col_line)
|
||||
|
||||
# Write out the index line if it's not empty.
|
||||
# Otherwise, we will print out an extraneous
|
||||
# blank line between the mi and the data rows.
|
||||
if encoded_labels and set(encoded_labels) != {""}:
|
||||
encoded_labels.extend([""] * len(columns))
|
||||
writer.writerow(encoded_labels)
|
||||
|
||||
def _save(self):
|
||||
|
||||
self._save_header()
|
||||
|
||||
nrows = len(self.data_index)
|
||||
|
||||
# write in chunksize bites
|
||||
chunksize = self.chunksize
|
||||
chunks = int(nrows / chunksize) + 1
|
||||
|
||||
for i in range(chunks):
|
||||
start_i = i * chunksize
|
||||
end_i = min((i + 1) * chunksize, nrows)
|
||||
if start_i >= end_i:
|
||||
break
|
||||
|
||||
self._save_chunk(start_i, end_i)
|
||||
|
||||
def _save_chunk(self, start_i, end_i):
|
||||
|
||||
data_index = self.data_index
|
||||
|
||||
# create the data for a chunk
|
||||
slicer = slice(start_i, end_i)
|
||||
for i in range(len(self.blocks)):
|
||||
b = self.blocks[i]
|
||||
d = b.to_native_types(
|
||||
slicer=slicer,
|
||||
na_rep=self.na_rep,
|
||||
float_format=self.float_format,
|
||||
decimal=self.decimal,
|
||||
date_format=self.date_format,
|
||||
quoting=self.quoting,
|
||||
)
|
||||
|
||||
for col_loc, col in zip(b.mgr_locs, d):
|
||||
# self.data is a preallocated list
|
||||
self.data[col_loc] = col
|
||||
|
||||
ix = data_index.to_native_types(
|
||||
slicer=slicer,
|
||||
na_rep=self.na_rep,
|
||||
float_format=self.float_format,
|
||||
decimal=self.decimal,
|
||||
date_format=self.date_format,
|
||||
quoting=self.quoting,
|
||||
)
|
||||
|
||||
libwriters.write_csv_rows(self.data, ix, self.nlevels, self.cols, self.writer)
|
742
venv/lib/python3.6/site-packages/pandas/io/formats/excel.py
Normal file
742
venv/lib/python3.6/site-packages/pandas/io/formats/excel.py
Normal file
@@ -0,0 +1,742 @@
|
||||
"""Utilities for conversion to writer-agnostic Excel representation
|
||||
"""
|
||||
|
||||
from functools import reduce
|
||||
import itertools
|
||||
import re
|
||||
import warnings
|
||||
|
||||
import numpy as np
|
||||
|
||||
from pandas.core.dtypes import missing
|
||||
from pandas.core.dtypes.common import is_float, is_scalar
|
||||
from pandas.core.dtypes.generic import ABCMultiIndex, ABCPeriodIndex
|
||||
|
||||
from pandas import Index
|
||||
import pandas.core.common as com
|
||||
|
||||
from pandas.io.formats.css import CSSResolver, CSSWarning
|
||||
from pandas.io.formats.format import get_level_lengths
|
||||
from pandas.io.formats.printing import pprint_thing
|
||||
|
||||
|
||||
class ExcelCell:
|
||||
__fields__ = ("row", "col", "val", "style", "mergestart", "mergeend")
|
||||
__slots__ = __fields__
|
||||
|
||||
def __init__(self, row, col, val, style=None, mergestart=None, mergeend=None):
|
||||
self.row = row
|
||||
self.col = col
|
||||
self.val = val
|
||||
self.style = style
|
||||
self.mergestart = mergestart
|
||||
self.mergeend = mergeend
|
||||
|
||||
|
||||
class CSSToExcelConverter:
|
||||
"""A callable for converting CSS declarations to ExcelWriter styles
|
||||
|
||||
Supports parts of CSS 2.2, with minimal CSS 3.0 support (e.g. text-shadow),
|
||||
focusing on font styling, backgrounds, borders and alignment.
|
||||
|
||||
Operates by first computing CSS styles in a fairly generic
|
||||
way (see :meth:`compute_css`) then determining Excel style
|
||||
properties from CSS properties (see :meth:`build_xlstyle`).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
inherited : str, optional
|
||||
CSS declarations understood to be the containing scope for the
|
||||
CSS processed by :meth:`__call__`.
|
||||
"""
|
||||
|
||||
# NB: Most of the methods here could be classmethods, as only __init__
|
||||
# and __call__ make use of instance attributes. We leave them as
|
||||
# instancemethods so that users can easily experiment with extensions
|
||||
# without monkey-patching.
|
||||
|
||||
def __init__(self, inherited=None):
|
||||
if inherited is not None:
|
||||
inherited = self.compute_css(inherited)
|
||||
|
||||
self.inherited = inherited
|
||||
|
||||
compute_css = CSSResolver()
|
||||
|
||||
def __call__(self, declarations_str):
|
||||
"""Convert CSS declarations to ExcelWriter style
|
||||
|
||||
Parameters
|
||||
----------
|
||||
declarations_str : str
|
||||
List of CSS declarations.
|
||||
e.g. "font-weight: bold; background: blue"
|
||||
|
||||
Returns
|
||||
-------
|
||||
xlstyle : dict
|
||||
A style as interpreted by ExcelWriter when found in
|
||||
ExcelCell.style.
|
||||
"""
|
||||
# TODO: memoize?
|
||||
properties = self.compute_css(declarations_str, self.inherited)
|
||||
return self.build_xlstyle(properties)
|
||||
|
||||
def build_xlstyle(self, props):
|
||||
out = {
|
||||
"alignment": self.build_alignment(props),
|
||||
"border": self.build_border(props),
|
||||
"fill": self.build_fill(props),
|
||||
"font": self.build_font(props),
|
||||
"number_format": self.build_number_format(props),
|
||||
}
|
||||
# TODO: handle cell width and height: needs support in pandas.io.excel
|
||||
|
||||
def remove_none(d):
|
||||
"""Remove key where value is None, through nested dicts"""
|
||||
for k, v in list(d.items()):
|
||||
if v is None:
|
||||
del d[k]
|
||||
elif isinstance(v, dict):
|
||||
remove_none(v)
|
||||
if not v:
|
||||
del d[k]
|
||||
|
||||
remove_none(out)
|
||||
return out
|
||||
|
||||
VERTICAL_MAP = {
|
||||
"top": "top",
|
||||
"text-top": "top",
|
||||
"middle": "center",
|
||||
"baseline": "bottom",
|
||||
"bottom": "bottom",
|
||||
"text-bottom": "bottom",
|
||||
# OpenXML also has 'justify', 'distributed'
|
||||
}
|
||||
|
||||
def build_alignment(self, props):
|
||||
# TODO: text-indent, padding-left -> alignment.indent
|
||||
return {
|
||||
"horizontal": props.get("text-align"),
|
||||
"vertical": self.VERTICAL_MAP.get(props.get("vertical-align")),
|
||||
"wrap_text": (
|
||||
None
|
||||
if props.get("white-space") is None
|
||||
else props["white-space"] not in ("nowrap", "pre", "pre-line")
|
||||
),
|
||||
}
|
||||
|
||||
def build_border(self, props):
|
||||
return {
|
||||
side: {
|
||||
"style": self._border_style(
|
||||
props.get("border-{side}-style".format(side=side)),
|
||||
props.get("border-{side}-width".format(side=side)),
|
||||
),
|
||||
"color": self.color_to_excel(
|
||||
props.get("border-{side}-color".format(side=side))
|
||||
),
|
||||
}
|
||||
for side in ["top", "right", "bottom", "left"]
|
||||
}
|
||||
|
||||
def _border_style(self, style, width):
|
||||
# convert styles and widths to openxml, one of:
|
||||
# 'dashDot'
|
||||
# 'dashDotDot'
|
||||
# 'dashed'
|
||||
# 'dotted'
|
||||
# 'double'
|
||||
# 'hair'
|
||||
# 'medium'
|
||||
# 'mediumDashDot'
|
||||
# 'mediumDashDotDot'
|
||||
# 'mediumDashed'
|
||||
# 'slantDashDot'
|
||||
# 'thick'
|
||||
# 'thin'
|
||||
if width is None and style is None:
|
||||
return None
|
||||
if style == "none" or style == "hidden":
|
||||
return None
|
||||
|
||||
if width is None:
|
||||
width = "2pt"
|
||||
width = float(width[:-2])
|
||||
if width < 1e-5:
|
||||
return None
|
||||
elif width < 1.3:
|
||||
width_name = "thin"
|
||||
elif width < 2.8:
|
||||
width_name = "medium"
|
||||
else:
|
||||
width_name = "thick"
|
||||
|
||||
if style in (None, "groove", "ridge", "inset", "outset"):
|
||||
# not handled
|
||||
style = "solid"
|
||||
|
||||
if style == "double":
|
||||
return "double"
|
||||
if style == "solid":
|
||||
return width_name
|
||||
if style == "dotted":
|
||||
if width_name in ("hair", "thin"):
|
||||
return "dotted"
|
||||
return "mediumDashDotDot"
|
||||
if style == "dashed":
|
||||
if width_name in ("hair", "thin"):
|
||||
return "dashed"
|
||||
return "mediumDashed"
|
||||
|
||||
def build_fill(self, props):
|
||||
# TODO: perhaps allow for special properties
|
||||
# -excel-pattern-bgcolor and -excel-pattern-type
|
||||
fill_color = props.get("background-color")
|
||||
if fill_color not in (None, "transparent", "none"):
|
||||
return {"fgColor": self.color_to_excel(fill_color), "patternType": "solid"}
|
||||
|
||||
BOLD_MAP = {
|
||||
"bold": True,
|
||||
"bolder": True,
|
||||
"600": True,
|
||||
"700": True,
|
||||
"800": True,
|
||||
"900": True,
|
||||
"normal": False,
|
||||
"lighter": False,
|
||||
"100": False,
|
||||
"200": False,
|
||||
"300": False,
|
||||
"400": False,
|
||||
"500": False,
|
||||
}
|
||||
ITALIC_MAP = {"normal": False, "italic": True, "oblique": True}
|
||||
|
||||
def build_font(self, props):
|
||||
size = props.get("font-size")
|
||||
if size is not None:
|
||||
assert size.endswith("pt")
|
||||
size = float(size[:-2])
|
||||
|
||||
font_names_tmp = re.findall(
|
||||
r"""(?x)
|
||||
(
|
||||
"(?:[^"]|\\")+"
|
||||
|
|
||||
'(?:[^']|\\')+'
|
||||
|
|
||||
[^'",]+
|
||||
)(?=,|\s*$)
|
||||
""",
|
||||
props.get("font-family", ""),
|
||||
)
|
||||
font_names = []
|
||||
for name in font_names_tmp:
|
||||
if name[:1] == '"':
|
||||
name = name[1:-1].replace('\\"', '"')
|
||||
elif name[:1] == "'":
|
||||
name = name[1:-1].replace("\\'", "'")
|
||||
else:
|
||||
name = name.strip()
|
||||
if name:
|
||||
font_names.append(name)
|
||||
|
||||
family = None
|
||||
for name in font_names:
|
||||
if name == "serif":
|
||||
family = 1 # roman
|
||||
break
|
||||
elif name == "sans-serif":
|
||||
family = 2 # swiss
|
||||
break
|
||||
elif name == "cursive":
|
||||
family = 4 # script
|
||||
break
|
||||
elif name == "fantasy":
|
||||
family = 5 # decorative
|
||||
break
|
||||
|
||||
decoration = props.get("text-decoration")
|
||||
if decoration is not None:
|
||||
decoration = decoration.split()
|
||||
else:
|
||||
decoration = ()
|
||||
|
||||
return {
|
||||
"name": font_names[0] if font_names else None,
|
||||
"family": family,
|
||||
"size": size,
|
||||
"bold": self.BOLD_MAP.get(props.get("font-weight")),
|
||||
"italic": self.ITALIC_MAP.get(props.get("font-style")),
|
||||
"underline": ("single" if "underline" in decoration else None),
|
||||
"strike": ("line-through" in decoration) or None,
|
||||
"color": self.color_to_excel(props.get("color")),
|
||||
# shadow if nonzero digit before shadow color
|
||||
"shadow": (
|
||||
bool(re.search("^[^#(]*[1-9]", props["text-shadow"]))
|
||||
if "text-shadow" in props
|
||||
else None
|
||||
),
|
||||
# 'vertAlign':,
|
||||
# 'charset': ,
|
||||
# 'scheme': ,
|
||||
# 'outline': ,
|
||||
# 'condense': ,
|
||||
}
|
||||
|
||||
NAMED_COLORS = {
|
||||
"maroon": "800000",
|
||||
"brown": "A52A2A",
|
||||
"red": "FF0000",
|
||||
"pink": "FFC0CB",
|
||||
"orange": "FFA500",
|
||||
"yellow": "FFFF00",
|
||||
"olive": "808000",
|
||||
"green": "008000",
|
||||
"purple": "800080",
|
||||
"fuchsia": "FF00FF",
|
||||
"lime": "00FF00",
|
||||
"teal": "008080",
|
||||
"aqua": "00FFFF",
|
||||
"blue": "0000FF",
|
||||
"navy": "000080",
|
||||
"black": "000000",
|
||||
"gray": "808080",
|
||||
"grey": "808080",
|
||||
"silver": "C0C0C0",
|
||||
"white": "FFFFFF",
|
||||
}
|
||||
|
||||
def color_to_excel(self, val):
|
||||
if val is None:
|
||||
return None
|
||||
if val.startswith("#") and len(val) == 7:
|
||||
return val[1:].upper()
|
||||
if val.startswith("#") and len(val) == 4:
|
||||
return (val[1] * 2 + val[2] * 2 + val[3] * 2).upper()
|
||||
try:
|
||||
return self.NAMED_COLORS[val]
|
||||
except KeyError:
|
||||
warnings.warn("Unhandled color format: {val!r}".format(val=val), CSSWarning)
|
||||
|
||||
def build_number_format(self, props):
|
||||
return {"format_code": props.get("number-format")}
|
||||
|
||||
|
||||
class ExcelFormatter:
|
||||
"""
|
||||
Class for formatting a DataFrame to a list of ExcelCells,
|
||||
|
||||
Parameters
|
||||
----------
|
||||
df : DataFrame or Styler
|
||||
na_rep: na representation
|
||||
float_format : string, default None
|
||||
Format string for floating point numbers
|
||||
cols : sequence, optional
|
||||
Columns to write
|
||||
header : boolean or list of string, default True
|
||||
Write out column names. If a list of string is given it is
|
||||
assumed to be aliases for the column names
|
||||
index : boolean, default True
|
||||
output row names (index)
|
||||
index_label : string or sequence, default None
|
||||
Column label for index column(s) if desired. If None is given, and
|
||||
`header` and `index` are True, then the index names are used. A
|
||||
sequence should be given if the DataFrame uses MultiIndex.
|
||||
merge_cells : boolean, default False
|
||||
Format MultiIndex and Hierarchical Rows as merged cells.
|
||||
inf_rep : string, default `'inf'`
|
||||
representation for np.inf values (which aren't representable in Excel)
|
||||
A `'-'` sign will be added in front of -inf.
|
||||
style_converter : callable, optional
|
||||
This translates Styler styles (CSS) into ExcelWriter styles.
|
||||
Defaults to ``CSSToExcelConverter()``.
|
||||
It should have signature css_declarations string -> excel style.
|
||||
This is only called for body cells.
|
||||
"""
|
||||
|
||||
max_rows = 2 ** 20
|
||||
max_cols = 2 ** 14
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
df,
|
||||
na_rep="",
|
||||
float_format=None,
|
||||
cols=None,
|
||||
header=True,
|
||||
index=True,
|
||||
index_label=None,
|
||||
merge_cells=False,
|
||||
inf_rep="inf",
|
||||
style_converter=None,
|
||||
):
|
||||
self.rowcounter = 0
|
||||
self.na_rep = na_rep
|
||||
if hasattr(df, "render"):
|
||||
self.styler = df
|
||||
df = df.data
|
||||
if style_converter is None:
|
||||
style_converter = CSSToExcelConverter()
|
||||
self.style_converter = style_converter
|
||||
else:
|
||||
self.styler = None
|
||||
self.df = df
|
||||
if cols is not None:
|
||||
|
||||
# all missing, raise
|
||||
if not len(Index(cols) & df.columns):
|
||||
raise KeyError("passes columns are not ALL present dataframe")
|
||||
|
||||
# deprecatedin gh-17295
|
||||
# 1 missing is ok (for now)
|
||||
if len(Index(cols) & df.columns) != len(cols):
|
||||
warnings.warn(
|
||||
"Not all names specified in 'columns' are found; "
|
||||
"this will raise a KeyError in the future",
|
||||
FutureWarning,
|
||||
)
|
||||
|
||||
self.df = df.reindex(columns=cols)
|
||||
self.columns = self.df.columns
|
||||
self.float_format = float_format
|
||||
self.index = index
|
||||
self.index_label = index_label
|
||||
self.header = header
|
||||
self.merge_cells = merge_cells
|
||||
self.inf_rep = inf_rep
|
||||
|
||||
@property
|
||||
def header_style(self):
|
||||
return {
|
||||
"font": {"bold": True},
|
||||
"borders": {
|
||||
"top": "thin",
|
||||
"right": "thin",
|
||||
"bottom": "thin",
|
||||
"left": "thin",
|
||||
},
|
||||
"alignment": {"horizontal": "center", "vertical": "top"},
|
||||
}
|
||||
|
||||
def _format_value(self, val):
|
||||
if is_scalar(val) and missing.isna(val):
|
||||
val = self.na_rep
|
||||
elif is_float(val):
|
||||
if missing.isposinf_scalar(val):
|
||||
val = self.inf_rep
|
||||
elif missing.isneginf_scalar(val):
|
||||
val = "-{inf}".format(inf=self.inf_rep)
|
||||
elif self.float_format is not None:
|
||||
val = float(self.float_format % val)
|
||||
if getattr(val, "tzinfo", None) is not None:
|
||||
raise ValueError(
|
||||
"Excel does not support datetimes with "
|
||||
"timezones. Please ensure that datetimes "
|
||||
"are timezone unaware before writing to Excel."
|
||||
)
|
||||
return val
|
||||
|
||||
def _format_header_mi(self):
|
||||
if self.columns.nlevels > 1:
|
||||
if not self.index:
|
||||
raise NotImplementedError(
|
||||
"Writing to Excel with MultiIndex"
|
||||
" columns and no index "
|
||||
"('index'=False) is not yet "
|
||||
"implemented."
|
||||
)
|
||||
|
||||
has_aliases = isinstance(self.header, (tuple, list, np.ndarray, Index))
|
||||
if not (has_aliases or self.header):
|
||||
return
|
||||
|
||||
columns = self.columns
|
||||
level_strs = columns.format(
|
||||
sparsify=self.merge_cells, adjoin=False, names=False
|
||||
)
|
||||
level_lengths = get_level_lengths(level_strs)
|
||||
coloffset = 0
|
||||
lnum = 0
|
||||
|
||||
if self.index and isinstance(self.df.index, ABCMultiIndex):
|
||||
coloffset = len(self.df.index[0]) - 1
|
||||
|
||||
if self.merge_cells:
|
||||
# Format multi-index as a merged cells.
|
||||
for lnum in range(len(level_lengths)):
|
||||
name = columns.names[lnum]
|
||||
yield ExcelCell(lnum, coloffset, name, self.header_style)
|
||||
|
||||
for lnum, (spans, levels, level_codes) in enumerate(
|
||||
zip(level_lengths, columns.levels, columns.codes)
|
||||
):
|
||||
values = levels.take(level_codes)
|
||||
for i in spans:
|
||||
if spans[i] > 1:
|
||||
yield ExcelCell(
|
||||
lnum,
|
||||
coloffset + i + 1,
|
||||
values[i],
|
||||
self.header_style,
|
||||
lnum,
|
||||
coloffset + i + spans[i],
|
||||
)
|
||||
else:
|
||||
yield ExcelCell(
|
||||
lnum, coloffset + i + 1, values[i], self.header_style
|
||||
)
|
||||
else:
|
||||
# Format in legacy format with dots to indicate levels.
|
||||
for i, values in enumerate(zip(*level_strs)):
|
||||
v = ".".join(map(pprint_thing, values))
|
||||
yield ExcelCell(lnum, coloffset + i + 1, v, self.header_style)
|
||||
|
||||
self.rowcounter = lnum
|
||||
|
||||
def _format_header_regular(self):
|
||||
has_aliases = isinstance(self.header, (tuple, list, np.ndarray, Index))
|
||||
if has_aliases or self.header:
|
||||
coloffset = 0
|
||||
|
||||
if self.index:
|
||||
coloffset = 1
|
||||
if isinstance(self.df.index, ABCMultiIndex):
|
||||
coloffset = len(self.df.index[0])
|
||||
|
||||
colnames = self.columns
|
||||
if has_aliases:
|
||||
if len(self.header) != len(self.columns):
|
||||
raise ValueError(
|
||||
"Writing {cols} cols but got {alias} "
|
||||
"aliases".format(cols=len(self.columns), alias=len(self.header))
|
||||
)
|
||||
else:
|
||||
colnames = self.header
|
||||
|
||||
for colindex, colname in enumerate(colnames):
|
||||
yield ExcelCell(
|
||||
self.rowcounter, colindex + coloffset, colname, self.header_style
|
||||
)
|
||||
|
||||
def _format_header(self):
|
||||
if isinstance(self.columns, ABCMultiIndex):
|
||||
gen = self._format_header_mi()
|
||||
else:
|
||||
gen = self._format_header_regular()
|
||||
|
||||
gen2 = ()
|
||||
if self.df.index.names:
|
||||
row = [x if x is not None else "" for x in self.df.index.names] + [
|
||||
""
|
||||
] * len(self.columns)
|
||||
if reduce(lambda x, y: x and y, map(lambda x: x != "", row)):
|
||||
gen2 = (
|
||||
ExcelCell(self.rowcounter, colindex, val, self.header_style)
|
||||
for colindex, val in enumerate(row)
|
||||
)
|
||||
self.rowcounter += 1
|
||||
return itertools.chain(gen, gen2)
|
||||
|
||||
def _format_body(self):
|
||||
|
||||
if isinstance(self.df.index, ABCMultiIndex):
|
||||
return self._format_hierarchical_rows()
|
||||
else:
|
||||
return self._format_regular_rows()
|
||||
|
||||
def _format_regular_rows(self):
|
||||
has_aliases = isinstance(self.header, (tuple, list, np.ndarray, Index))
|
||||
if has_aliases or self.header:
|
||||
self.rowcounter += 1
|
||||
|
||||
# output index and index_label?
|
||||
if self.index:
|
||||
# check aliases
|
||||
# if list only take first as this is not a MultiIndex
|
||||
if self.index_label and isinstance(
|
||||
self.index_label, (list, tuple, np.ndarray, Index)
|
||||
):
|
||||
index_label = self.index_label[0]
|
||||
# if string good to go
|
||||
elif self.index_label and isinstance(self.index_label, str):
|
||||
index_label = self.index_label
|
||||
else:
|
||||
index_label = self.df.index.names[0]
|
||||
|
||||
if isinstance(self.columns, ABCMultiIndex):
|
||||
self.rowcounter += 1
|
||||
|
||||
if index_label and self.header is not False:
|
||||
yield ExcelCell(self.rowcounter - 1, 0, index_label, self.header_style)
|
||||
|
||||
# write index_values
|
||||
index_values = self.df.index
|
||||
if isinstance(self.df.index, ABCPeriodIndex):
|
||||
index_values = self.df.index.to_timestamp()
|
||||
|
||||
for idx, idxval in enumerate(index_values):
|
||||
yield ExcelCell(self.rowcounter + idx, 0, idxval, self.header_style)
|
||||
|
||||
coloffset = 1
|
||||
else:
|
||||
coloffset = 0
|
||||
|
||||
for cell in self._generate_body(coloffset):
|
||||
yield cell
|
||||
|
||||
def _format_hierarchical_rows(self):
|
||||
has_aliases = isinstance(self.header, (tuple, list, np.ndarray, Index))
|
||||
if has_aliases or self.header:
|
||||
self.rowcounter += 1
|
||||
|
||||
gcolidx = 0
|
||||
|
||||
if self.index:
|
||||
index_labels = self.df.index.names
|
||||
# check for aliases
|
||||
if self.index_label and isinstance(
|
||||
self.index_label, (list, tuple, np.ndarray, Index)
|
||||
):
|
||||
index_labels = self.index_label
|
||||
|
||||
# MultiIndex columns require an extra row
|
||||
# with index names (blank if None) for
|
||||
# unambiguous round-trip, unless not merging,
|
||||
# in which case the names all go on one row Issue #11328
|
||||
if isinstance(self.columns, ABCMultiIndex) and self.merge_cells:
|
||||
self.rowcounter += 1
|
||||
|
||||
# if index labels are not empty go ahead and dump
|
||||
if com._any_not_none(*index_labels) and self.header is not False:
|
||||
|
||||
for cidx, name in enumerate(index_labels):
|
||||
yield ExcelCell(self.rowcounter - 1, cidx, name, self.header_style)
|
||||
|
||||
if self.merge_cells:
|
||||
# Format hierarchical rows as merged cells.
|
||||
level_strs = self.df.index.format(
|
||||
sparsify=True, adjoin=False, names=False
|
||||
)
|
||||
level_lengths = get_level_lengths(level_strs)
|
||||
|
||||
for spans, levels, level_codes in zip(
|
||||
level_lengths, self.df.index.levels, self.df.index.codes
|
||||
):
|
||||
|
||||
values = levels.take(
|
||||
level_codes, allow_fill=levels._can_hold_na, fill_value=True
|
||||
)
|
||||
|
||||
for i in spans:
|
||||
if spans[i] > 1:
|
||||
yield ExcelCell(
|
||||
self.rowcounter + i,
|
||||
gcolidx,
|
||||
values[i],
|
||||
self.header_style,
|
||||
self.rowcounter + i + spans[i] - 1,
|
||||
gcolidx,
|
||||
)
|
||||
else:
|
||||
yield ExcelCell(
|
||||
self.rowcounter + i,
|
||||
gcolidx,
|
||||
values[i],
|
||||
self.header_style,
|
||||
)
|
||||
gcolidx += 1
|
||||
|
||||
else:
|
||||
# Format hierarchical rows with non-merged values.
|
||||
for indexcolvals in zip(*self.df.index):
|
||||
for idx, indexcolval in enumerate(indexcolvals):
|
||||
yield ExcelCell(
|
||||
self.rowcounter + idx,
|
||||
gcolidx,
|
||||
indexcolval,
|
||||
self.header_style,
|
||||
)
|
||||
gcolidx += 1
|
||||
|
||||
for cell in self._generate_body(gcolidx):
|
||||
yield cell
|
||||
|
||||
def _generate_body(self, coloffset):
|
||||
if self.styler is None:
|
||||
styles = None
|
||||
else:
|
||||
styles = self.styler._compute().ctx
|
||||
if not styles:
|
||||
styles = None
|
||||
xlstyle = None
|
||||
|
||||
# Write the body of the frame data series by series.
|
||||
for colidx in range(len(self.columns)):
|
||||
series = self.df.iloc[:, colidx]
|
||||
for i, val in enumerate(series):
|
||||
if styles is not None:
|
||||
xlstyle = self.style_converter(";".join(styles[i, colidx]))
|
||||
yield ExcelCell(self.rowcounter + i, colidx + coloffset, val, xlstyle)
|
||||
|
||||
def get_formatted_cells(self):
|
||||
for cell in itertools.chain(self._format_header(), self._format_body()):
|
||||
cell.val = self._format_value(cell.val)
|
||||
yield cell
|
||||
|
||||
def write(
|
||||
self,
|
||||
writer,
|
||||
sheet_name="Sheet1",
|
||||
startrow=0,
|
||||
startcol=0,
|
||||
freeze_panes=None,
|
||||
engine=None,
|
||||
):
|
||||
"""
|
||||
writer : string or ExcelWriter object
|
||||
File path or existing ExcelWriter
|
||||
sheet_name : string, default 'Sheet1'
|
||||
Name of sheet which will contain DataFrame
|
||||
startrow :
|
||||
upper left cell row to dump data frame
|
||||
startcol :
|
||||
upper left cell column to dump data frame
|
||||
freeze_panes : tuple of integer (length 2), default None
|
||||
Specifies the one-based bottommost row and rightmost column that
|
||||
is to be frozen
|
||||
engine : string, default None
|
||||
write engine to use if writer is a path - you can also set this
|
||||
via the options ``io.excel.xlsx.writer``, ``io.excel.xls.writer``,
|
||||
and ``io.excel.xlsm.writer``.
|
||||
"""
|
||||
from pandas.io.excel import ExcelWriter
|
||||
from pandas.io.common import _stringify_path
|
||||
|
||||
num_rows, num_cols = self.df.shape
|
||||
if num_rows > self.max_rows or num_cols > self.max_cols:
|
||||
raise ValueError(
|
||||
"This sheet is too large! Your sheet size is: "
|
||||
+ "{}, {} ".format(num_rows, num_cols)
|
||||
+ "Max sheet size is: {}, {}".format(self.max_rows, self.max_cols)
|
||||
)
|
||||
|
||||
if isinstance(writer, ExcelWriter):
|
||||
need_save = False
|
||||
else:
|
||||
writer = ExcelWriter(_stringify_path(writer), engine=engine)
|
||||
need_save = True
|
||||
|
||||
formatted_cells = self.get_formatted_cells()
|
||||
writer.write_cells(
|
||||
formatted_cells,
|
||||
sheet_name,
|
||||
startrow=startrow,
|
||||
startcol=startcol,
|
||||
freeze_panes=freeze_panes,
|
||||
)
|
||||
if need_save:
|
||||
writer.save()
|
1816
venv/lib/python3.6/site-packages/pandas/io/formats/format.py
Normal file
1816
venv/lib/python3.6/site-packages/pandas/io/formats/format.py
Normal file
File diff suppressed because it is too large
Load Diff
608
venv/lib/python3.6/site-packages/pandas/io/formats/html.py
Normal file
608
venv/lib/python3.6/site-packages/pandas/io/formats/html.py
Normal file
@@ -0,0 +1,608 @@
|
||||
"""
|
||||
Module for formatting output data in HTML.
|
||||
"""
|
||||
|
||||
from collections import OrderedDict
|
||||
from textwrap import dedent
|
||||
from typing import Dict, List, Optional, Tuple, Union
|
||||
|
||||
from pandas._config import get_option
|
||||
|
||||
from pandas.core.dtypes.generic import ABCIndex, ABCMultiIndex
|
||||
|
||||
from pandas import option_context
|
||||
|
||||
from pandas.io.common import _is_url
|
||||
from pandas.io.formats.format import (
|
||||
DataFrameFormatter,
|
||||
TableFormatter,
|
||||
get_level_lengths,
|
||||
)
|
||||
from pandas.io.formats.printing import pprint_thing
|
||||
|
||||
|
||||
class HTMLFormatter(TableFormatter):
|
||||
"""
|
||||
Internal class for formatting output data in html.
|
||||
This class is intended for shared functionality between
|
||||
DataFrame.to_html() and DataFrame._repr_html_().
|
||||
Any logic in common with other output formatting methods
|
||||
should ideally be inherited from classes in format.py
|
||||
and this class responsible for only producing html markup.
|
||||
"""
|
||||
|
||||
indent_delta = 2
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
formatter: DataFrameFormatter,
|
||||
classes: Optional[Union[str, List, Tuple]] = None,
|
||||
border: Optional[bool] = None,
|
||||
) -> None:
|
||||
self.fmt = formatter
|
||||
self.classes = classes
|
||||
|
||||
self.frame = self.fmt.frame
|
||||
self.columns = self.fmt.tr_frame.columns
|
||||
self.elements = [] # type: List[str]
|
||||
self.bold_rows = self.fmt.kwds.get("bold_rows", False)
|
||||
self.escape = self.fmt.kwds.get("escape", True)
|
||||
self.show_dimensions = self.fmt.show_dimensions
|
||||
if border is None:
|
||||
border = get_option("display.html.border")
|
||||
self.border = border
|
||||
self.table_id = self.fmt.table_id
|
||||
self.render_links = self.fmt.render_links
|
||||
if isinstance(self.fmt.col_space, int):
|
||||
self.fmt.col_space = "{colspace}px".format(colspace=self.fmt.col_space)
|
||||
|
||||
@property
|
||||
def show_row_idx_names(self) -> bool:
|
||||
return self.fmt.show_row_idx_names
|
||||
|
||||
@property
|
||||
def show_col_idx_names(self) -> bool:
|
||||
return self.fmt.show_col_idx_names
|
||||
|
||||
@property
|
||||
def row_levels(self) -> int:
|
||||
if self.fmt.index:
|
||||
# showing (row) index
|
||||
return self.frame.index.nlevels
|
||||
elif self.show_col_idx_names:
|
||||
# see gh-22579
|
||||
# Column misalignment also occurs for
|
||||
# a standard index when the columns index is named.
|
||||
# If the row index is not displayed a column of
|
||||
# blank cells need to be included before the DataFrame values.
|
||||
return 1
|
||||
# not showing (row) index
|
||||
return 0
|
||||
|
||||
def _get_columns_formatted_values(self) -> ABCIndex:
|
||||
return self.columns
|
||||
|
||||
@property
|
||||
def is_truncated(self) -> bool:
|
||||
return self.fmt.is_truncated
|
||||
|
||||
@property
|
||||
def ncols(self) -> int:
|
||||
return len(self.fmt.tr_frame.columns)
|
||||
|
||||
def write(self, s: str, indent: int = 0) -> None:
|
||||
rs = pprint_thing(s)
|
||||
self.elements.append(" " * indent + rs)
|
||||
|
||||
def write_th(
|
||||
self, s: str, header: bool = False, indent: int = 0, tags: Optional[str] = None
|
||||
) -> None:
|
||||
"""
|
||||
Method for writting a formatted <th> cell.
|
||||
|
||||
If col_space is set on the formatter then that is used for
|
||||
the value of min-width.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
s : object
|
||||
The data to be written inside the cell.
|
||||
header : boolean, default False
|
||||
Set to True if the <th> is for use inside <thead>. This will
|
||||
cause min-width to be set if there is one.
|
||||
indent : int, default 0
|
||||
The indentation level of the cell.
|
||||
tags : string, default None
|
||||
Tags to include in the cell.
|
||||
|
||||
Returns
|
||||
-------
|
||||
A written <th> cell.
|
||||
"""
|
||||
if header and self.fmt.col_space is not None:
|
||||
tags = tags or ""
|
||||
tags += 'style="min-width: {colspace};"'.format(colspace=self.fmt.col_space)
|
||||
|
||||
self._write_cell(s, kind="th", indent=indent, tags=tags)
|
||||
|
||||
def write_td(self, s: str, indent: int = 0, tags: Optional[str] = None) -> None:
|
||||
self._write_cell(s, kind="td", indent=indent, tags=tags)
|
||||
|
||||
def _write_cell(
|
||||
self, s: str, kind: str = "td", indent: int = 0, tags: Optional[str] = None
|
||||
) -> None:
|
||||
if tags is not None:
|
||||
start_tag = "<{kind} {tags}>".format(kind=kind, tags=tags)
|
||||
else:
|
||||
start_tag = "<{kind}>".format(kind=kind)
|
||||
|
||||
if self.escape:
|
||||
# escape & first to prevent double escaping of &
|
||||
esc = OrderedDict(
|
||||
[("&", r"&"), ("<", r"<"), (">", r">")]
|
||||
) # type: Union[OrderedDict[str, str], Dict]
|
||||
else:
|
||||
esc = {}
|
||||
|
||||
rs = pprint_thing(s, escape_chars=esc).strip()
|
||||
|
||||
if self.render_links and _is_url(rs):
|
||||
rs_unescaped = pprint_thing(s, escape_chars={}).strip()
|
||||
start_tag += '<a href="{url}" target="_blank">'.format(url=rs_unescaped)
|
||||
end_a = "</a>"
|
||||
else:
|
||||
end_a = ""
|
||||
|
||||
self.write(
|
||||
"{start}{rs}{end_a}</{kind}>".format(
|
||||
start=start_tag, rs=rs, end_a=end_a, kind=kind
|
||||
),
|
||||
indent,
|
||||
)
|
||||
|
||||
def write_tr(
|
||||
self,
|
||||
line: List[str],
|
||||
indent: int = 0,
|
||||
indent_delta: int = 0,
|
||||
header: bool = False,
|
||||
align: Optional[str] = None,
|
||||
tags: Optional[Dict[int, str]] = None,
|
||||
nindex_levels: int = 0,
|
||||
) -> None:
|
||||
if tags is None:
|
||||
tags = {}
|
||||
|
||||
if align is None:
|
||||
self.write("<tr>", indent)
|
||||
else:
|
||||
self.write('<tr style="text-align: {align};">'.format(align=align), indent)
|
||||
indent += indent_delta
|
||||
|
||||
for i, s in enumerate(line):
|
||||
val_tag = tags.get(i, None)
|
||||
if header or (self.bold_rows and i < nindex_levels):
|
||||
self.write_th(s, indent=indent, header=header, tags=val_tag)
|
||||
else:
|
||||
self.write_td(s, indent, tags=val_tag)
|
||||
|
||||
indent -= indent_delta
|
||||
self.write("</tr>", indent)
|
||||
|
||||
def render(self) -> List[str]:
|
||||
self._write_table()
|
||||
|
||||
if self.should_show_dimensions:
|
||||
by = chr(215) # ×
|
||||
self.write(
|
||||
"<p>{rows} rows {by} {cols} columns</p>".format(
|
||||
rows=len(self.frame), by=by, cols=len(self.frame.columns)
|
||||
)
|
||||
)
|
||||
|
||||
return self.elements
|
||||
|
||||
def _write_table(self, indent: int = 0) -> None:
|
||||
_classes = ["dataframe"] # Default class.
|
||||
use_mathjax = get_option("display.html.use_mathjax")
|
||||
if not use_mathjax:
|
||||
_classes.append("tex2jax_ignore")
|
||||
if self.classes is not None:
|
||||
if isinstance(self.classes, str):
|
||||
self.classes = self.classes.split()
|
||||
if not isinstance(self.classes, (list, tuple)):
|
||||
raise TypeError(
|
||||
"classes must be a string, list, or tuple, "
|
||||
"not {typ}".format(typ=type(self.classes))
|
||||
)
|
||||
_classes.extend(self.classes)
|
||||
|
||||
if self.table_id is None:
|
||||
id_section = ""
|
||||
else:
|
||||
id_section = ' id="{table_id}"'.format(table_id=self.table_id)
|
||||
|
||||
self.write(
|
||||
'<table border="{border}" class="{cls}"{id_section}>'.format(
|
||||
border=self.border, cls=" ".join(_classes), id_section=id_section
|
||||
),
|
||||
indent,
|
||||
)
|
||||
|
||||
if self.fmt.header or self.show_row_idx_names:
|
||||
self._write_header(indent + self.indent_delta)
|
||||
|
||||
self._write_body(indent + self.indent_delta)
|
||||
|
||||
self.write("</table>", indent)
|
||||
|
||||
def _write_col_header(self, indent: int) -> None:
|
||||
truncate_h = self.fmt.truncate_h
|
||||
if isinstance(self.columns, ABCMultiIndex):
|
||||
template = 'colspan="{span:d}" halign="left"'
|
||||
|
||||
if self.fmt.sparsify:
|
||||
# GH3547
|
||||
sentinel = object()
|
||||
else:
|
||||
sentinel = False
|
||||
levels = self.columns.format(sparsify=sentinel, adjoin=False, names=False)
|
||||
level_lengths = get_level_lengths(levels, sentinel)
|
||||
inner_lvl = len(level_lengths) - 1
|
||||
for lnum, (records, values) in enumerate(zip(level_lengths, levels)):
|
||||
if truncate_h:
|
||||
# modify the header lines
|
||||
ins_col = self.fmt.tr_col_num
|
||||
if self.fmt.sparsify:
|
||||
recs_new = {}
|
||||
# Increment tags after ... col.
|
||||
for tag, span in list(records.items()):
|
||||
if tag >= ins_col:
|
||||
recs_new[tag + 1] = span
|
||||
elif tag + span > ins_col:
|
||||
recs_new[tag] = span + 1
|
||||
if lnum == inner_lvl:
|
||||
values = (
|
||||
values[:ins_col] + ("...",) + values[ins_col:]
|
||||
)
|
||||
else:
|
||||
# sparse col headers do not receive a ...
|
||||
values = (
|
||||
values[:ins_col]
|
||||
+ (values[ins_col - 1],)
|
||||
+ values[ins_col:]
|
||||
)
|
||||
else:
|
||||
recs_new[tag] = span
|
||||
# if ins_col lies between tags, all col headers
|
||||
# get ...
|
||||
if tag + span == ins_col:
|
||||
recs_new[ins_col] = 1
|
||||
values = values[:ins_col] + ("...",) + values[ins_col:]
|
||||
records = recs_new
|
||||
inner_lvl = len(level_lengths) - 1
|
||||
if lnum == inner_lvl:
|
||||
records[ins_col] = 1
|
||||
else:
|
||||
recs_new = {}
|
||||
for tag, span in list(records.items()):
|
||||
if tag >= ins_col:
|
||||
recs_new[tag + 1] = span
|
||||
else:
|
||||
recs_new[tag] = span
|
||||
recs_new[ins_col] = 1
|
||||
records = recs_new
|
||||
values = values[:ins_col] + ["..."] + values[ins_col:]
|
||||
|
||||
# see gh-22579
|
||||
# Column Offset Bug with to_html(index=False) with
|
||||
# MultiIndex Columns and Index.
|
||||
# Initially fill row with blank cells before column names.
|
||||
# TODO: Refactor to remove code duplication with code
|
||||
# block below for standard columns index.
|
||||
row = [""] * (self.row_levels - 1)
|
||||
if self.fmt.index or self.show_col_idx_names:
|
||||
# see gh-22747
|
||||
# If to_html(index_names=False) do not show columns
|
||||
# index names.
|
||||
# TODO: Refactor to use _get_column_name_list from
|
||||
# DataFrameFormatter class and create a
|
||||
# _get_formatted_column_labels function for code
|
||||
# parity with DataFrameFormatter class.
|
||||
if self.fmt.show_index_names:
|
||||
name = self.columns.names[lnum]
|
||||
row.append(pprint_thing(name or ""))
|
||||
else:
|
||||
row.append("")
|
||||
|
||||
tags = {}
|
||||
j = len(row)
|
||||
for i, v in enumerate(values):
|
||||
if i in records:
|
||||
if records[i] > 1:
|
||||
tags[j] = template.format(span=records[i])
|
||||
else:
|
||||
continue
|
||||
j += 1
|
||||
row.append(v)
|
||||
self.write_tr(row, indent, self.indent_delta, tags=tags, header=True)
|
||||
else:
|
||||
# see gh-22579
|
||||
# Column misalignment also occurs for
|
||||
# a standard index when the columns index is named.
|
||||
# Initially fill row with blank cells before column names.
|
||||
# TODO: Refactor to remove code duplication with code block
|
||||
# above for columns MultiIndex.
|
||||
row = [""] * (self.row_levels - 1)
|
||||
if self.fmt.index or self.show_col_idx_names:
|
||||
# see gh-22747
|
||||
# If to_html(index_names=False) do not show columns
|
||||
# index names.
|
||||
# TODO: Refactor to use _get_column_name_list from
|
||||
# DataFrameFormatter class.
|
||||
if self.fmt.show_index_names:
|
||||
row.append(self.columns.name or "")
|
||||
else:
|
||||
row.append("")
|
||||
row.extend(self._get_columns_formatted_values())
|
||||
align = self.fmt.justify
|
||||
|
||||
if truncate_h:
|
||||
ins_col = self.row_levels + self.fmt.tr_col_num
|
||||
row.insert(ins_col, "...")
|
||||
|
||||
self.write_tr(row, indent, self.indent_delta, header=True, align=align)
|
||||
|
||||
def _write_row_header(self, indent: int) -> None:
|
||||
truncate_h = self.fmt.truncate_h
|
||||
row = [x if x is not None else "" for x in self.frame.index.names] + [""] * (
|
||||
self.ncols + (1 if truncate_h else 0)
|
||||
)
|
||||
self.write_tr(row, indent, self.indent_delta, header=True)
|
||||
|
||||
def _write_header(self, indent: int) -> None:
|
||||
self.write("<thead>", indent)
|
||||
|
||||
if self.fmt.header:
|
||||
self._write_col_header(indent + self.indent_delta)
|
||||
|
||||
if self.show_row_idx_names:
|
||||
self._write_row_header(indent + self.indent_delta)
|
||||
|
||||
self.write("</thead>", indent)
|
||||
|
||||
def _get_formatted_values(self) -> Dict[int, List[str]]:
|
||||
with option_context("display.max_colwidth", 999999):
|
||||
fmt_values = {i: self.fmt._format_col(i) for i in range(self.ncols)}
|
||||
return fmt_values
|
||||
|
||||
def _write_body(self, indent: int) -> None:
|
||||
self.write("<tbody>", indent)
|
||||
fmt_values = self._get_formatted_values()
|
||||
|
||||
# write values
|
||||
if self.fmt.index and isinstance(self.frame.index, ABCMultiIndex):
|
||||
self._write_hierarchical_rows(fmt_values, indent + self.indent_delta)
|
||||
else:
|
||||
self._write_regular_rows(fmt_values, indent + self.indent_delta)
|
||||
|
||||
self.write("</tbody>", indent)
|
||||
|
||||
def _write_regular_rows(
|
||||
self, fmt_values: Dict[int, List[str]], indent: int
|
||||
) -> None:
|
||||
truncate_h = self.fmt.truncate_h
|
||||
truncate_v = self.fmt.truncate_v
|
||||
|
||||
nrows = len(self.fmt.tr_frame)
|
||||
|
||||
if self.fmt.index:
|
||||
fmt = self.fmt._get_formatter("__index__")
|
||||
if fmt is not None:
|
||||
index_values = self.fmt.tr_frame.index.map(fmt)
|
||||
else:
|
||||
index_values = self.fmt.tr_frame.index.format()
|
||||
|
||||
row = [] # type: List[str]
|
||||
for i in range(nrows):
|
||||
|
||||
if truncate_v and i == (self.fmt.tr_row_num):
|
||||
str_sep_row = ["..."] * len(row)
|
||||
self.write_tr(
|
||||
str_sep_row,
|
||||
indent,
|
||||
self.indent_delta,
|
||||
tags=None,
|
||||
nindex_levels=self.row_levels,
|
||||
)
|
||||
|
||||
row = []
|
||||
if self.fmt.index:
|
||||
row.append(index_values[i])
|
||||
# see gh-22579
|
||||
# Column misalignment also occurs for
|
||||
# a standard index when the columns index is named.
|
||||
# Add blank cell before data cells.
|
||||
elif self.show_col_idx_names:
|
||||
row.append("")
|
||||
row.extend(fmt_values[j][i] for j in range(self.ncols))
|
||||
|
||||
if truncate_h:
|
||||
dot_col_ix = self.fmt.tr_col_num + self.row_levels
|
||||
row.insert(dot_col_ix, "...")
|
||||
self.write_tr(
|
||||
row, indent, self.indent_delta, tags=None, nindex_levels=self.row_levels
|
||||
)
|
||||
|
||||
def _write_hierarchical_rows(
|
||||
self, fmt_values: Dict[int, List[str]], indent: int
|
||||
) -> None:
|
||||
template = 'rowspan="{span}" valign="top"'
|
||||
|
||||
truncate_h = self.fmt.truncate_h
|
||||
truncate_v = self.fmt.truncate_v
|
||||
frame = self.fmt.tr_frame
|
||||
nrows = len(frame)
|
||||
|
||||
idx_values = frame.index.format(sparsify=False, adjoin=False, names=False)
|
||||
idx_values = list(zip(*idx_values))
|
||||
|
||||
if self.fmt.sparsify:
|
||||
# GH3547
|
||||
sentinel = object()
|
||||
levels = frame.index.format(sparsify=sentinel, adjoin=False, names=False)
|
||||
|
||||
level_lengths = get_level_lengths(levels, sentinel)
|
||||
inner_lvl = len(level_lengths) - 1
|
||||
if truncate_v:
|
||||
# Insert ... row and adjust idx_values and
|
||||
# level_lengths to take this into account.
|
||||
ins_row = self.fmt.tr_row_num
|
||||
inserted = False
|
||||
for lnum, records in enumerate(level_lengths):
|
||||
rec_new = {}
|
||||
for tag, span in list(records.items()):
|
||||
if tag >= ins_row:
|
||||
rec_new[tag + 1] = span
|
||||
elif tag + span > ins_row:
|
||||
rec_new[tag] = span + 1
|
||||
|
||||
# GH 14882 - Make sure insertion done once
|
||||
if not inserted:
|
||||
dot_row = list(idx_values[ins_row - 1])
|
||||
dot_row[-1] = "..."
|
||||
idx_values.insert(ins_row, tuple(dot_row))
|
||||
inserted = True
|
||||
else:
|
||||
dot_row = list(idx_values[ins_row])
|
||||
dot_row[inner_lvl - lnum] = "..."
|
||||
idx_values[ins_row] = tuple(dot_row)
|
||||
else:
|
||||
rec_new[tag] = span
|
||||
# If ins_row lies between tags, all cols idx cols
|
||||
# receive ...
|
||||
if tag + span == ins_row:
|
||||
rec_new[ins_row] = 1
|
||||
if lnum == 0:
|
||||
idx_values.insert(
|
||||
ins_row, tuple(["..."] * len(level_lengths))
|
||||
)
|
||||
|
||||
# GH 14882 - Place ... in correct level
|
||||
elif inserted:
|
||||
dot_row = list(idx_values[ins_row])
|
||||
dot_row[inner_lvl - lnum] = "..."
|
||||
idx_values[ins_row] = tuple(dot_row)
|
||||
level_lengths[lnum] = rec_new
|
||||
|
||||
level_lengths[inner_lvl][ins_row] = 1
|
||||
for ix_col in range(len(fmt_values)):
|
||||
fmt_values[ix_col].insert(ins_row, "...")
|
||||
nrows += 1
|
||||
|
||||
for i in range(nrows):
|
||||
row = []
|
||||
tags = {}
|
||||
|
||||
sparse_offset = 0
|
||||
j = 0
|
||||
for records, v in zip(level_lengths, idx_values[i]):
|
||||
if i in records:
|
||||
if records[i] > 1:
|
||||
tags[j] = template.format(span=records[i])
|
||||
else:
|
||||
sparse_offset += 1
|
||||
continue
|
||||
|
||||
j += 1
|
||||
row.append(v)
|
||||
|
||||
row.extend(fmt_values[j][i] for j in range(self.ncols))
|
||||
if truncate_h:
|
||||
row.insert(
|
||||
self.row_levels - sparse_offset + self.fmt.tr_col_num, "..."
|
||||
)
|
||||
self.write_tr(
|
||||
row,
|
||||
indent,
|
||||
self.indent_delta,
|
||||
tags=tags,
|
||||
nindex_levels=len(levels) - sparse_offset,
|
||||
)
|
||||
else:
|
||||
row = []
|
||||
for i in range(len(frame)):
|
||||
if truncate_v and i == (self.fmt.tr_row_num):
|
||||
str_sep_row = ["..."] * len(row)
|
||||
self.write_tr(
|
||||
str_sep_row,
|
||||
indent,
|
||||
self.indent_delta,
|
||||
tags=None,
|
||||
nindex_levels=self.row_levels,
|
||||
)
|
||||
|
||||
idx_values = list(
|
||||
zip(*frame.index.format(sparsify=False, adjoin=False, names=False))
|
||||
)
|
||||
row = []
|
||||
row.extend(idx_values[i])
|
||||
row.extend(fmt_values[j][i] for j in range(self.ncols))
|
||||
if truncate_h:
|
||||
row.insert(self.row_levels + self.fmt.tr_col_num, "...")
|
||||
self.write_tr(
|
||||
row,
|
||||
indent,
|
||||
self.indent_delta,
|
||||
tags=None,
|
||||
nindex_levels=frame.index.nlevels,
|
||||
)
|
||||
|
||||
|
||||
class NotebookFormatter(HTMLFormatter):
|
||||
"""
|
||||
Internal class for formatting output data in html for display in Jupyter
|
||||
Notebooks. This class is intended for functionality specific to
|
||||
DataFrame._repr_html_() and DataFrame.to_html(notebook=True)
|
||||
"""
|
||||
|
||||
def _get_formatted_values(self) -> Dict[int, List[str]]:
|
||||
return {i: self.fmt._format_col(i) for i in range(self.ncols)}
|
||||
|
||||
def _get_columns_formatted_values(self) -> List[str]:
|
||||
return self.columns.format()
|
||||
|
||||
def write_style(self) -> None:
|
||||
# We use the "scoped" attribute here so that the desired
|
||||
# style properties for the data frame are not then applied
|
||||
# throughout the entire notebook.
|
||||
template_first = """\
|
||||
<style scoped>"""
|
||||
template_last = """\
|
||||
</style>"""
|
||||
template_select = """\
|
||||
.dataframe %s {
|
||||
%s: %s;
|
||||
}"""
|
||||
element_props = [
|
||||
("tbody tr th:only-of-type", "vertical-align", "middle"),
|
||||
("tbody tr th", "vertical-align", "top"),
|
||||
]
|
||||
if isinstance(self.columns, ABCMultiIndex):
|
||||
element_props.append(("thead tr th", "text-align", "left"))
|
||||
if self.show_row_idx_names:
|
||||
element_props.append(
|
||||
("thead tr:last-of-type th", "text-align", "right")
|
||||
)
|
||||
else:
|
||||
element_props.append(("thead th", "text-align", "right"))
|
||||
template_mid = "\n\n".join(map(lambda t: template_select % t, element_props))
|
||||
template = dedent("\n".join((template_first, template_mid, template_last)))
|
||||
self.write(template)
|
||||
|
||||
def render(self) -> List[str]:
|
||||
self.write("<div>")
|
||||
self.write_style()
|
||||
super().render()
|
||||
self.write("</div>")
|
||||
return self.elements
|
265
venv/lib/python3.6/site-packages/pandas/io/formats/latex.py
Normal file
265
venv/lib/python3.6/site-packages/pandas/io/formats/latex.py
Normal file
@@ -0,0 +1,265 @@
|
||||
"""
|
||||
Module for formatting output data in Latex.
|
||||
"""
|
||||
import numpy as np
|
||||
|
||||
from pandas.core.dtypes.generic import ABCMultiIndex
|
||||
|
||||
from pandas.io.formats.format import TableFormatter
|
||||
|
||||
|
||||
class LatexFormatter(TableFormatter):
|
||||
""" Used to render a DataFrame to a LaTeX tabular/longtable environment
|
||||
output.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
formatter : `DataFrameFormatter`
|
||||
column_format : str, default None
|
||||
The columns format as specified in `LaTeX table format
|
||||
<https://en.wikibooks.org/wiki/LaTeX/Tables>`__ e.g 'rcl' for 3 columns
|
||||
longtable : boolean, default False
|
||||
Use a longtable environment instead of tabular.
|
||||
|
||||
See Also
|
||||
--------
|
||||
HTMLFormatter
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
formatter,
|
||||
column_format=None,
|
||||
longtable=False,
|
||||
multicolumn=False,
|
||||
multicolumn_format=None,
|
||||
multirow=False,
|
||||
):
|
||||
self.fmt = formatter
|
||||
self.frame = self.fmt.frame
|
||||
self.bold_rows = self.fmt.kwds.get("bold_rows", False)
|
||||
self.column_format = column_format
|
||||
self.longtable = longtable
|
||||
self.multicolumn = multicolumn
|
||||
self.multicolumn_format = multicolumn_format
|
||||
self.multirow = multirow
|
||||
|
||||
def write_result(self, buf):
|
||||
"""
|
||||
Render a DataFrame to a LaTeX tabular/longtable environment output.
|
||||
"""
|
||||
|
||||
# string representation of the columns
|
||||
if len(self.frame.columns) == 0 or len(self.frame.index) == 0:
|
||||
info_line = "Empty {name}\nColumns: {col}\nIndex: {idx}".format(
|
||||
name=type(self.frame).__name__,
|
||||
col=self.frame.columns,
|
||||
idx=self.frame.index,
|
||||
)
|
||||
strcols = [[info_line]]
|
||||
else:
|
||||
strcols = self.fmt._to_str_columns()
|
||||
|
||||
def get_col_type(dtype):
|
||||
if issubclass(dtype.type, np.number):
|
||||
return "r"
|
||||
else:
|
||||
return "l"
|
||||
|
||||
# reestablish the MultiIndex that has been joined by _to_str_column
|
||||
if self.fmt.index and isinstance(self.frame.index, ABCMultiIndex):
|
||||
out = self.frame.index.format(
|
||||
adjoin=False,
|
||||
sparsify=self.fmt.sparsify,
|
||||
names=self.fmt.has_index_names,
|
||||
na_rep=self.fmt.na_rep,
|
||||
)
|
||||
|
||||
# index.format will sparsify repeated entries with empty strings
|
||||
# so pad these with some empty space
|
||||
def pad_empties(x):
|
||||
for pad in reversed(x):
|
||||
if pad:
|
||||
break
|
||||
return [x[0]] + [i if i else " " * len(pad) for i in x[1:]]
|
||||
|
||||
out = (pad_empties(i) for i in out)
|
||||
|
||||
# Add empty spaces for each column level
|
||||
clevels = self.frame.columns.nlevels
|
||||
out = [[" " * len(i[-1])] * clevels + i for i in out]
|
||||
|
||||
# Add the column names to the last index column
|
||||
cnames = self.frame.columns.names
|
||||
if any(cnames):
|
||||
new_names = [i if i else "{}" for i in cnames]
|
||||
out[self.frame.index.nlevels - 1][:clevels] = new_names
|
||||
|
||||
# Get rid of old multiindex column and add new ones
|
||||
strcols = out + strcols[1:]
|
||||
|
||||
column_format = self.column_format
|
||||
if column_format is None:
|
||||
dtypes = self.frame.dtypes._values
|
||||
column_format = "".join(map(get_col_type, dtypes))
|
||||
if self.fmt.index:
|
||||
index_format = "l" * self.frame.index.nlevels
|
||||
column_format = index_format + column_format
|
||||
elif not isinstance(column_format, str): # pragma: no cover
|
||||
raise AssertionError(
|
||||
"column_format must be str or unicode, "
|
||||
"not {typ}".format(typ=type(column_format))
|
||||
)
|
||||
|
||||
if not self.longtable:
|
||||
buf.write("\\begin{{tabular}}{{{fmt}}}\n".format(fmt=column_format))
|
||||
buf.write("\\toprule\n")
|
||||
else:
|
||||
buf.write("\\begin{{longtable}}{{{fmt}}}\n".format(fmt=column_format))
|
||||
buf.write("\\toprule\n")
|
||||
|
||||
ilevels = self.frame.index.nlevels
|
||||
clevels = self.frame.columns.nlevels
|
||||
nlevels = clevels
|
||||
if self.fmt.has_index_names and self.fmt.show_index_names:
|
||||
nlevels += 1
|
||||
strrows = list(zip(*strcols))
|
||||
self.clinebuf = []
|
||||
|
||||
for i, row in enumerate(strrows):
|
||||
if i == nlevels and self.fmt.header:
|
||||
buf.write("\\midrule\n") # End of header
|
||||
if self.longtable:
|
||||
buf.write("\\endhead\n")
|
||||
buf.write("\\midrule\n")
|
||||
buf.write(
|
||||
"\\multicolumn{{{n}}}{{r}}{{{{Continued on next "
|
||||
"page}}}} \\\\\n".format(n=len(row))
|
||||
)
|
||||
buf.write("\\midrule\n")
|
||||
buf.write("\\endfoot\n\n")
|
||||
buf.write("\\bottomrule\n")
|
||||
buf.write("\\endlastfoot\n")
|
||||
if self.fmt.kwds.get("escape", True):
|
||||
# escape backslashes first
|
||||
crow = [
|
||||
(
|
||||
x.replace("\\", "\\textbackslash ")
|
||||
.replace("_", "\\_")
|
||||
.replace("%", "\\%")
|
||||
.replace("$", "\\$")
|
||||
.replace("#", "\\#")
|
||||
.replace("{", "\\{")
|
||||
.replace("}", "\\}")
|
||||
.replace("~", "\\textasciitilde ")
|
||||
.replace("^", "\\textasciicircum ")
|
||||
.replace("&", "\\&")
|
||||
if (x and x != "{}")
|
||||
else "{}"
|
||||
)
|
||||
for x in row
|
||||
]
|
||||
else:
|
||||
crow = [x if x else "{}" for x in row]
|
||||
if self.bold_rows and self.fmt.index:
|
||||
# bold row labels
|
||||
crow = [
|
||||
"\\textbf{{{x}}}".format(x=x)
|
||||
if j < ilevels and x.strip() not in ["", "{}"]
|
||||
else x
|
||||
for j, x in enumerate(crow)
|
||||
]
|
||||
if i < clevels and self.fmt.header and self.multicolumn:
|
||||
# sum up columns to multicolumns
|
||||
crow = self._format_multicolumn(crow, ilevels)
|
||||
if i >= nlevels and self.fmt.index and self.multirow and ilevels > 1:
|
||||
# sum up rows to multirows
|
||||
crow = self._format_multirow(crow, ilevels, i, strrows)
|
||||
buf.write(" & ".join(crow))
|
||||
buf.write(" \\\\\n")
|
||||
if self.multirow and i < len(strrows) - 1:
|
||||
self._print_cline(buf, i, len(strcols))
|
||||
|
||||
if not self.longtable:
|
||||
buf.write("\\bottomrule\n")
|
||||
buf.write("\\end{tabular}\n")
|
||||
else:
|
||||
buf.write("\\end{longtable}\n")
|
||||
|
||||
def _format_multicolumn(self, row, ilevels):
|
||||
r"""
|
||||
Combine columns belonging to a group to a single multicolumn entry
|
||||
according to self.multicolumn_format
|
||||
|
||||
e.g.:
|
||||
a & & & b & c &
|
||||
will become
|
||||
\multicolumn{3}{l}{a} & b & \multicolumn{2}{l}{c}
|
||||
"""
|
||||
row2 = list(row[:ilevels])
|
||||
ncol = 1
|
||||
coltext = ""
|
||||
|
||||
def append_col():
|
||||
# write multicolumn if needed
|
||||
if ncol > 1:
|
||||
row2.append(
|
||||
"\\multicolumn{{{ncol:d}}}{{{fmt:s}}}{{{txt:s}}}".format(
|
||||
ncol=ncol, fmt=self.multicolumn_format, txt=coltext.strip()
|
||||
)
|
||||
)
|
||||
# don't modify where not needed
|
||||
else:
|
||||
row2.append(coltext)
|
||||
|
||||
for c in row[ilevels:]:
|
||||
# if next col has text, write the previous
|
||||
if c.strip():
|
||||
if coltext:
|
||||
append_col()
|
||||
coltext = c
|
||||
ncol = 1
|
||||
# if not, add it to the previous multicolumn
|
||||
else:
|
||||
ncol += 1
|
||||
# write last column name
|
||||
if coltext:
|
||||
append_col()
|
||||
return row2
|
||||
|
||||
def _format_multirow(self, row, ilevels, i, rows):
|
||||
r"""
|
||||
Check following rows, whether row should be a multirow
|
||||
|
||||
e.g.: becomes:
|
||||
a & 0 & \multirow{2}{*}{a} & 0 &
|
||||
& 1 & & 1 &
|
||||
b & 0 & \cline{1-2}
|
||||
b & 0 &
|
||||
"""
|
||||
for j in range(ilevels):
|
||||
if row[j].strip():
|
||||
nrow = 1
|
||||
for r in rows[i + 1 :]:
|
||||
if not r[j].strip():
|
||||
nrow += 1
|
||||
else:
|
||||
break
|
||||
if nrow > 1:
|
||||
# overwrite non-multirow entry
|
||||
row[j] = "\\multirow{{{nrow:d}}}{{*}}{{{row:s}}}".format(
|
||||
nrow=nrow, row=row[j].strip()
|
||||
)
|
||||
# save when to end the current block with \cline
|
||||
self.clinebuf.append([i + nrow - 1, j + 1])
|
||||
return row
|
||||
|
||||
def _print_cline(self, buf, i, icol):
|
||||
"""
|
||||
Print clines after multirow-blocks are finished
|
||||
"""
|
||||
for cl in self.clinebuf:
|
||||
if cl[0] == i:
|
||||
buf.write("\\cline{{{cl:d}-{icol:d}}}\n".format(cl=cl[1], icol=icol))
|
||||
# remove entries that have been written to buffer
|
||||
self.clinebuf = [x for x in self.clinebuf if x[0] != i]
|
517
venv/lib/python3.6/site-packages/pandas/io/formats/printing.py
Normal file
517
venv/lib/python3.6/site-packages/pandas/io/formats/printing.py
Normal file
@@ -0,0 +1,517 @@
|
||||
"""
|
||||
printing tools
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
from pandas._config import get_option
|
||||
|
||||
from pandas.core.dtypes.inference import is_sequence
|
||||
|
||||
|
||||
def adjoin(space, *lists, **kwargs):
|
||||
"""
|
||||
Glues together two sets of strings using the amount of space requested.
|
||||
The idea is to prettify.
|
||||
|
||||
----------
|
||||
space : int
|
||||
number of spaces for padding
|
||||
lists : str
|
||||
list of str which being joined
|
||||
strlen : callable
|
||||
function used to calculate the length of each str. Needed for unicode
|
||||
handling.
|
||||
justfunc : callable
|
||||
function used to justify str. Needed for unicode handling.
|
||||
"""
|
||||
strlen = kwargs.pop("strlen", len)
|
||||
justfunc = kwargs.pop("justfunc", justify)
|
||||
|
||||
out_lines = []
|
||||
newLists = []
|
||||
lengths = [max(map(strlen, x)) + space for x in lists[:-1]]
|
||||
# not the last one
|
||||
lengths.append(max(map(len, lists[-1])))
|
||||
maxLen = max(map(len, lists))
|
||||
for i, lst in enumerate(lists):
|
||||
nl = justfunc(lst, lengths[i], mode="left")
|
||||
nl.extend([" " * lengths[i]] * (maxLen - len(lst)))
|
||||
newLists.append(nl)
|
||||
toJoin = zip(*newLists)
|
||||
for lines in toJoin:
|
||||
out_lines.append(_join_unicode(lines))
|
||||
return _join_unicode(out_lines, sep="\n")
|
||||
|
||||
|
||||
def justify(texts, max_len, mode="right"):
|
||||
"""
|
||||
Perform ljust, center, rjust against string or list-like
|
||||
"""
|
||||
if mode == "left":
|
||||
return [x.ljust(max_len) for x in texts]
|
||||
elif mode == "center":
|
||||
return [x.center(max_len) for x in texts]
|
||||
else:
|
||||
return [x.rjust(max_len) for x in texts]
|
||||
|
||||
|
||||
def _join_unicode(lines, sep=""):
|
||||
try:
|
||||
return sep.join(lines)
|
||||
except UnicodeDecodeError:
|
||||
sep = str(sep)
|
||||
return sep.join([x.decode("utf-8") if isinstance(x, str) else x for x in lines])
|
||||
|
||||
|
||||
# Unicode consolidation
|
||||
# ---------------------
|
||||
#
|
||||
# pprinting utility functions for generating Unicode text or
|
||||
# bytes(3.x)/str(2.x) representations of objects.
|
||||
# Try to use these as much as possible rather then rolling your own.
|
||||
#
|
||||
# When to use
|
||||
# -----------
|
||||
#
|
||||
# 1) If you're writing code internal to pandas (no I/O directly involved),
|
||||
# use pprint_thing().
|
||||
#
|
||||
# It will always return unicode text which can handled by other
|
||||
# parts of the package without breakage.
|
||||
#
|
||||
# 2) if you need to write something out to file, use
|
||||
# pprint_thing_encoded(encoding).
|
||||
#
|
||||
# If no encoding is specified, it defaults to utf-8. Since encoding pure
|
||||
# ascii with utf-8 is a no-op you can safely use the default utf-8 if you're
|
||||
# working with straight ascii.
|
||||
|
||||
|
||||
def _pprint_seq(seq, _nest_lvl=0, max_seq_items=None, **kwds):
|
||||
"""
|
||||
internal. pprinter for iterables. you should probably use pprint_thing()
|
||||
rather then calling this directly.
|
||||
|
||||
bounds length of printed sequence, depending on options
|
||||
"""
|
||||
if isinstance(seq, set):
|
||||
fmt = "{{{body}}}"
|
||||
else:
|
||||
fmt = "[{body}]" if hasattr(seq, "__setitem__") else "({body})"
|
||||
|
||||
if max_seq_items is False:
|
||||
nitems = len(seq)
|
||||
else:
|
||||
nitems = max_seq_items or get_option("max_seq_items") or len(seq)
|
||||
|
||||
s = iter(seq)
|
||||
# handle sets, no slicing
|
||||
r = [
|
||||
pprint_thing(next(s), _nest_lvl + 1, max_seq_items=max_seq_items, **kwds)
|
||||
for i in range(min(nitems, len(seq)))
|
||||
]
|
||||
body = ", ".join(r)
|
||||
|
||||
if nitems < len(seq):
|
||||
body += ", ..."
|
||||
elif isinstance(seq, tuple) and len(seq) == 1:
|
||||
body += ","
|
||||
|
||||
return fmt.format(body=body)
|
||||
|
||||
|
||||
def _pprint_dict(seq, _nest_lvl=0, max_seq_items=None, **kwds):
|
||||
"""
|
||||
internal. pprinter for iterables. you should probably use pprint_thing()
|
||||
rather then calling this directly.
|
||||
"""
|
||||
fmt = "{{{things}}}"
|
||||
pairs = []
|
||||
|
||||
pfmt = "{key}: {val}"
|
||||
|
||||
if max_seq_items is False:
|
||||
nitems = len(seq)
|
||||
else:
|
||||
nitems = max_seq_items or get_option("max_seq_items") or len(seq)
|
||||
|
||||
for k, v in list(seq.items())[:nitems]:
|
||||
pairs.append(
|
||||
pfmt.format(
|
||||
key=pprint_thing(k, _nest_lvl + 1, max_seq_items=max_seq_items, **kwds),
|
||||
val=pprint_thing(v, _nest_lvl + 1, max_seq_items=max_seq_items, **kwds),
|
||||
)
|
||||
)
|
||||
|
||||
if nitems < len(seq):
|
||||
return fmt.format(things=", ".join(pairs) + ", ...")
|
||||
else:
|
||||
return fmt.format(things=", ".join(pairs))
|
||||
|
||||
|
||||
def pprint_thing(
|
||||
thing,
|
||||
_nest_lvl=0,
|
||||
escape_chars=None,
|
||||
default_escapes=False,
|
||||
quote_strings=False,
|
||||
max_seq_items=None,
|
||||
):
|
||||
"""
|
||||
This function is the sanctioned way of converting objects
|
||||
to a unicode representation.
|
||||
|
||||
properly handles nested sequences containing unicode strings
|
||||
(unicode(object) does not)
|
||||
|
||||
Parameters
|
||||
----------
|
||||
thing : anything to be formatted
|
||||
_nest_lvl : internal use only. pprint_thing() is mutually-recursive
|
||||
with pprint_sequence, this argument is used to keep track of the
|
||||
current nesting level, and limit it.
|
||||
escape_chars : list or dict, optional
|
||||
Characters to escape. If a dict is passed the values are the
|
||||
replacements
|
||||
default_escapes : bool, default False
|
||||
Whether the input escape characters replaces or adds to the defaults
|
||||
max_seq_items : False, int, default None
|
||||
Pass thru to other pretty printers to limit sequence printing
|
||||
|
||||
Returns
|
||||
-------
|
||||
result - unicode str
|
||||
|
||||
"""
|
||||
|
||||
def as_escaped_unicode(thing, escape_chars=escape_chars):
|
||||
# Unicode is fine, else we try to decode using utf-8 and 'replace'
|
||||
# if that's not it either, we have no way of knowing and the user
|
||||
# should deal with it himself.
|
||||
|
||||
try:
|
||||
result = str(thing) # we should try this first
|
||||
except UnicodeDecodeError:
|
||||
# either utf-8 or we replace errors
|
||||
result = str(thing).decode("utf-8", "replace")
|
||||
|
||||
translate = {"\t": r"\t", "\n": r"\n", "\r": r"\r"}
|
||||
if isinstance(escape_chars, dict):
|
||||
if default_escapes:
|
||||
translate.update(escape_chars)
|
||||
else:
|
||||
translate = escape_chars
|
||||
escape_chars = list(escape_chars.keys())
|
||||
else:
|
||||
escape_chars = escape_chars or tuple()
|
||||
for c in escape_chars:
|
||||
result = result.replace(c, translate[c])
|
||||
|
||||
return str(result)
|
||||
|
||||
if hasattr(thing, "__next__"):
|
||||
return str(thing)
|
||||
elif isinstance(thing, dict) and _nest_lvl < get_option(
|
||||
"display.pprint_nest_depth"
|
||||
):
|
||||
result = _pprint_dict(
|
||||
thing, _nest_lvl, quote_strings=True, max_seq_items=max_seq_items
|
||||
)
|
||||
elif is_sequence(thing) and _nest_lvl < get_option("display.pprint_nest_depth"):
|
||||
result = _pprint_seq(
|
||||
thing,
|
||||
_nest_lvl,
|
||||
escape_chars=escape_chars,
|
||||
quote_strings=quote_strings,
|
||||
max_seq_items=max_seq_items,
|
||||
)
|
||||
elif isinstance(thing, str) and quote_strings:
|
||||
result = "'{thing}'".format(thing=as_escaped_unicode(thing))
|
||||
else:
|
||||
result = as_escaped_unicode(thing)
|
||||
|
||||
return str(result) # always unicode
|
||||
|
||||
|
||||
def pprint_thing_encoded(object, encoding="utf-8", errors="replace", **kwds):
|
||||
value = pprint_thing(object) # get unicode representation of object
|
||||
return value.encode(encoding, errors, **kwds)
|
||||
|
||||
|
||||
def _enable_data_resource_formatter(enable):
|
||||
if "IPython" not in sys.modules:
|
||||
# definitely not in IPython
|
||||
return
|
||||
from IPython import get_ipython
|
||||
|
||||
ip = get_ipython()
|
||||
if ip is None:
|
||||
# still not in IPython
|
||||
return
|
||||
|
||||
formatters = ip.display_formatter.formatters
|
||||
mimetype = "application/vnd.dataresource+json"
|
||||
|
||||
if enable:
|
||||
if mimetype not in formatters:
|
||||
# define tableschema formatter
|
||||
from IPython.core.formatters import BaseFormatter
|
||||
|
||||
class TableSchemaFormatter(BaseFormatter):
|
||||
print_method = "_repr_data_resource_"
|
||||
_return_type = (dict,)
|
||||
|
||||
# register it:
|
||||
formatters[mimetype] = TableSchemaFormatter()
|
||||
# enable it if it's been disabled:
|
||||
formatters[mimetype].enabled = True
|
||||
else:
|
||||
# unregister tableschema mime-type
|
||||
if mimetype in formatters:
|
||||
formatters[mimetype].enabled = False
|
||||
|
||||
|
||||
default_pprint = lambda x, max_seq_items=None: pprint_thing(
|
||||
x, escape_chars=("\t", "\r", "\n"), quote_strings=True, max_seq_items=max_seq_items
|
||||
)
|
||||
|
||||
|
||||
def format_object_summary(
|
||||
obj,
|
||||
formatter,
|
||||
is_justify=True,
|
||||
name=None,
|
||||
indent_for_name=True,
|
||||
line_break_each_value=False,
|
||||
):
|
||||
"""
|
||||
Return the formatted obj as a unicode string
|
||||
|
||||
Parameters
|
||||
----------
|
||||
obj : object
|
||||
must be iterable and support __getitem__
|
||||
formatter : callable
|
||||
string formatter for an element
|
||||
is_justify : boolean
|
||||
should justify the display
|
||||
name : name, optional
|
||||
defaults to the class name of the obj
|
||||
indent_for_name : bool, default True
|
||||
Whether subsequent lines should be be indented to
|
||||
align with the name.
|
||||
line_break_each_value : bool, default False
|
||||
If True, inserts a line break for each value of ``obj``.
|
||||
If False, only break lines when the a line of values gets wider
|
||||
than the display width.
|
||||
|
||||
.. versionadded:: 0.25.0
|
||||
|
||||
Returns
|
||||
-------
|
||||
summary string
|
||||
|
||||
"""
|
||||
from pandas.io.formats.console import get_console_size
|
||||
from pandas.io.formats.format import _get_adjustment
|
||||
|
||||
display_width, _ = get_console_size()
|
||||
if display_width is None:
|
||||
display_width = get_option("display.width") or 80
|
||||
if name is None:
|
||||
name = obj.__class__.__name__
|
||||
|
||||
if indent_for_name:
|
||||
name_len = len(name)
|
||||
space1 = "\n%s" % (" " * (name_len + 1))
|
||||
space2 = "\n%s" % (" " * (name_len + 2))
|
||||
else:
|
||||
space1 = "\n"
|
||||
space2 = "\n " # space for the opening '['
|
||||
|
||||
n = len(obj)
|
||||
if line_break_each_value:
|
||||
# If we want to vertically align on each value of obj, we need to
|
||||
# separate values by a line break and indent the values
|
||||
sep = ",\n " + " " * len(name)
|
||||
else:
|
||||
sep = ","
|
||||
max_seq_items = get_option("display.max_seq_items") or n
|
||||
|
||||
# are we a truncated display
|
||||
is_truncated = n > max_seq_items
|
||||
|
||||
# adj can optionally handle unicode eastern asian width
|
||||
adj = _get_adjustment()
|
||||
|
||||
def _extend_line(s, line, value, display_width, next_line_prefix):
|
||||
|
||||
if adj.len(line.rstrip()) + adj.len(value.rstrip()) >= display_width:
|
||||
s += line.rstrip()
|
||||
line = next_line_prefix
|
||||
line += value
|
||||
return s, line
|
||||
|
||||
def best_len(values):
|
||||
if values:
|
||||
return max(adj.len(x) for x in values)
|
||||
else:
|
||||
return 0
|
||||
|
||||
close = ", "
|
||||
|
||||
if n == 0:
|
||||
summary = "[]{}".format(close)
|
||||
elif n == 1 and not line_break_each_value:
|
||||
first = formatter(obj[0])
|
||||
summary = "[{}]{}".format(first, close)
|
||||
elif n == 2 and not line_break_each_value:
|
||||
first = formatter(obj[0])
|
||||
last = formatter(obj[-1])
|
||||
summary = "[{}, {}]{}".format(first, last, close)
|
||||
else:
|
||||
|
||||
if n > max_seq_items:
|
||||
n = min(max_seq_items // 2, 10)
|
||||
head = [formatter(x) for x in obj[:n]]
|
||||
tail = [formatter(x) for x in obj[-n:]]
|
||||
else:
|
||||
head = []
|
||||
tail = [formatter(x) for x in obj]
|
||||
|
||||
# adjust all values to max length if needed
|
||||
if is_justify:
|
||||
if line_break_each_value:
|
||||
# Justify each string in the values of head and tail, so the
|
||||
# strings will right align when head and tail are stacked
|
||||
# vertically.
|
||||
head, tail = _justify(head, tail)
|
||||
elif is_truncated or not (
|
||||
len(", ".join(head)) < display_width
|
||||
and len(", ".join(tail)) < display_width
|
||||
):
|
||||
# Each string in head and tail should align with each other
|
||||
max_length = max(best_len(head), best_len(tail))
|
||||
head = [x.rjust(max_length) for x in head]
|
||||
tail = [x.rjust(max_length) for x in tail]
|
||||
# If we are not truncated and we are only a single
|
||||
# line, then don't justify
|
||||
|
||||
if line_break_each_value:
|
||||
# Now head and tail are of type List[Tuple[str]]. Below we
|
||||
# convert them into List[str], so there will be one string per
|
||||
# value. Also truncate items horizontally if wider than
|
||||
# max_space
|
||||
max_space = display_width - len(space2)
|
||||
value = tail[0]
|
||||
for max_items in reversed(range(1, len(value) + 1)):
|
||||
pprinted_seq = _pprint_seq(value, max_seq_items=max_items)
|
||||
if len(pprinted_seq) < max_space:
|
||||
break
|
||||
head = [_pprint_seq(x, max_seq_items=max_items) for x in head]
|
||||
tail = [_pprint_seq(x, max_seq_items=max_items) for x in tail]
|
||||
|
||||
summary = ""
|
||||
line = space2
|
||||
|
||||
for max_items in range(len(head)):
|
||||
word = head[max_items] + sep + " "
|
||||
summary, line = _extend_line(summary, line, word, display_width, space2)
|
||||
|
||||
if is_truncated:
|
||||
# remove trailing space of last line
|
||||
summary += line.rstrip() + space2 + "..."
|
||||
line = space2
|
||||
|
||||
for max_items in range(len(tail) - 1):
|
||||
word = tail[max_items] + sep + " "
|
||||
summary, line = _extend_line(summary, line, word, display_width, space2)
|
||||
|
||||
# last value: no sep added + 1 space of width used for trailing ','
|
||||
summary, line = _extend_line(summary, line, tail[-1], display_width - 2, space2)
|
||||
summary += line
|
||||
|
||||
# right now close is either '' or ', '
|
||||
# Now we want to include the ']', but not the maybe space.
|
||||
close = "]" + close.rstrip(" ")
|
||||
summary += close
|
||||
|
||||
if len(summary) > (display_width) or line_break_each_value:
|
||||
summary += space1
|
||||
else: # one row
|
||||
summary += " "
|
||||
|
||||
# remove initial space
|
||||
summary = "[" + summary[len(space2) :]
|
||||
|
||||
return summary
|
||||
|
||||
|
||||
def _justify(head, tail):
|
||||
"""
|
||||
Justify items in head and tail, so they are right-aligned when stacked.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
head : list-like of list-likes of strings
|
||||
tail : list-like of list-likes of strings
|
||||
|
||||
Returns
|
||||
-------
|
||||
tuple of list of tuples of strings
|
||||
Same as head and tail, but items are right aligned when stacked
|
||||
vertically.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> _justify([['a', 'b']], [['abc', 'abcd']])
|
||||
([(' a', ' b')], [('abc', 'abcd')])
|
||||
"""
|
||||
combined = head + tail
|
||||
|
||||
# For each position for the sequences in ``combined``,
|
||||
# find the length of the largest string.
|
||||
max_length = [0] * len(combined[0])
|
||||
for inner_seq in combined:
|
||||
length = [len(item) for item in inner_seq]
|
||||
max_length = [max(x, y) for x, y in zip(max_length, length)]
|
||||
|
||||
# justify each item in each list-like in head and tail using max_length
|
||||
head = [
|
||||
tuple(x.rjust(max_len) for x, max_len in zip(seq, max_length)) for seq in head
|
||||
]
|
||||
tail = [
|
||||
tuple(x.rjust(max_len) for x, max_len in zip(seq, max_length)) for seq in tail
|
||||
]
|
||||
return head, tail
|
||||
|
||||
|
||||
def format_object_attrs(obj, include_dtype=True):
|
||||
"""
|
||||
Return a list of tuples of the (attr, formatted_value)
|
||||
for common attrs, including dtype, name, length
|
||||
|
||||
Parameters
|
||||
----------
|
||||
obj : object
|
||||
must be iterable
|
||||
include_dtype : bool
|
||||
If False, dtype won't be in the returned list
|
||||
|
||||
Returns
|
||||
-------
|
||||
list
|
||||
|
||||
"""
|
||||
attrs = []
|
||||
if hasattr(obj, "dtype") and include_dtype:
|
||||
attrs.append(("dtype", "'{}'".format(obj.dtype)))
|
||||
if getattr(obj, "name", None) is not None:
|
||||
attrs.append(("name", default_pprint(obj.name)))
|
||||
elif getattr(obj, "names", None) is not None and any(obj.names):
|
||||
attrs.append(("names", default_pprint(obj.names)))
|
||||
max_seq_items = get_option("display.max_seq_items") or len(obj)
|
||||
if len(obj) > max_seq_items:
|
||||
attrs.append(("length", len(obj)))
|
||||
return attrs
|
1474
venv/lib/python3.6/site-packages/pandas/io/formats/style.py
Normal file
1474
venv/lib/python3.6/site-packages/pandas/io/formats/style.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,70 @@
|
||||
{# Update the template_structure.html document too #}
|
||||
{%- block before_style -%}{%- endblock before_style -%}
|
||||
{% block style %}
|
||||
<style type="text/css" >
|
||||
{% block table_styles %}
|
||||
{% for s in table_styles %}
|
||||
#T_{{uuid}} {{s.selector}} {
|
||||
{% for p,val in s.props %}
|
||||
{{p}}: {{val}};
|
||||
{% endfor -%}
|
||||
}
|
||||
{%- endfor -%}
|
||||
{% endblock table_styles %}
|
||||
{% block before_cellstyle %}{% endblock before_cellstyle %}
|
||||
{% block cellstyle %}
|
||||
{%- for s in cellstyle %}
|
||||
#T_{{uuid}}{{s.selector}} {
|
||||
{% for p,val in s.props %}
|
||||
{{p}}: {{val}};
|
||||
{% endfor %}
|
||||
}
|
||||
{%- endfor -%}
|
||||
{%- endblock cellstyle %}
|
||||
</style>
|
||||
{%- endblock style %}
|
||||
{%- block before_table %}{% endblock before_table %}
|
||||
{%- block table %}
|
||||
<table id="T_{{uuid}}" {% if table_attributes %}{{ table_attributes }}{% endif %}>
|
||||
{%- block caption %}
|
||||
{%- if caption -%}
|
||||
<caption>{{caption}}</caption>
|
||||
{%- endif -%}
|
||||
{%- endblock caption %}
|
||||
{%- block thead %}
|
||||
<thead>
|
||||
{%- block before_head_rows %}{% endblock %}
|
||||
{%- for r in head %}
|
||||
{%- block head_tr scoped %}
|
||||
<tr>
|
||||
{%- for c in r %}
|
||||
{%- if c.is_visible != False %}
|
||||
<{{ c.type }} class="{{c.class}}" {{ c.attributes|join(" ") }}>{{c.value}}</{{ c.type }}>
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
</tr>
|
||||
{%- endblock head_tr %}
|
||||
{%- endfor %}
|
||||
{%- block after_head_rows %}{% endblock %}
|
||||
</thead>
|
||||
{%- endblock thead %}
|
||||
{%- block tbody %}
|
||||
<tbody>
|
||||
{% block before_rows %}{% endblock before_rows %}
|
||||
{% for r in body %}
|
||||
{% block tr scoped %}
|
||||
<tr>
|
||||
{% for c in r %}
|
||||
{% if c.is_visible != False %}
|
||||
<{{ c.type }} {% if c.id is defined -%} id="T_{{ uuid }}{{ c.id }}" {%- endif %} class="{{ c.class }}" {{ c.attributes|join(" ") }}>{{ c.display_value }}</{{ c.type }}>
|
||||
{% endif %}
|
||||
{%- endfor %}
|
||||
</tr>
|
||||
{% endblock tr %}
|
||||
{%- endfor %}
|
||||
{%- block after_rows %}{%- endblock after_rows %}
|
||||
</tbody>
|
||||
{%- endblock tbody %}
|
||||
</table>
|
||||
{%- endblock table %}
|
||||
{%- block after_table %}{% endblock after_table %}
|
Reference in New Issue
Block a user