mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-10-29 13:32:08 +00:00
* Add a feature flag loaded from . env for preferred data source * Update the load command to use the preferred source * Add a test * Black syntax checker updates * Update test syntax * Update load command documentation * Add a variable to read a sources file from * Add a default sources.json file * Add a helper function to read the source file and get the most relevant value * Add some comments * Remove older preferred data source file setting * Add a default source file for data and use it if no user preference specifies a specific file * Remove duplicate source file and old settings values * Update the test file * Update documentation string * Update tests/openbb_terminal/test_settings_controller.py * Black syntax formatting issues * Fix a test case * Fix a typo * Fix Flake8 errors * Fix Flake8 errors * Pyupgrade syntax fixes * Additional linter corrections * More pylint issues * Improve predefined default sources * refactor parse_known_args_and_warn for documentation * Update openbb_terminal/helper_funcs.py * Add a feature flag loaded from . env for preferred data source * Update the load command to use the preferred source * Add a test * Black syntax checker updates * Update test syntax * Update load command documentation * Add a variable to read a sources file from * Add a default sources.json file * Add a helper function to read the source file and get the most relevant value * Add some comments * Remove older preferred data source file setting * Add a default source file for data and use it if no user preference specifies a specific file * Remove duplicate source file and old settings values * Update the test file * Update documentation string * Update tests/openbb_terminal/test_settings_controller.py * Black syntax formatting issues * Fix a test case * Fix a typo * Fix Flake8 errors * Fix Flake8 errors * Pyupgrade syntax fixes * Additional linter corrections * More pylint issues * Improve predefined default sources * refactor parse_known_args_and_warn for documentation * Update openbb_terminal/helper_funcs.py * Forex controller * Tests : Fixing `parse_known_args_and_warn` issue * Comparison Analysis controller * Tests : Comparison Analysis * Tests : Stocks/DarkPoolShorts + Stocks/Options * Tests : fix call_load tests * Linting : fixing line size Co-authored-by: DidierRLopes <dro.lopes@campus.fct.unl.pt> Co-authored-by: Chavithra PARANA <chavithra@gmail.com>
1732 lines
64 KiB
Python
1732 lines
64 KiB
Python
"""Econometrics Controller Module"""
|
||
__docformat__ = "numpy"
|
||
|
||
# pylint: disable=too-many-lines, too-many-branches, inconsistent-return-statements
|
||
|
||
import argparse
|
||
import logging
|
||
from itertools import chain
|
||
import os
|
||
from pathlib import Path
|
||
from typing import List, Dict, Any
|
||
|
||
import numpy as np
|
||
import pandas as pd
|
||
from prompt_toolkit.completion import NestedCompleter
|
||
|
||
import openbb_terminal.econometrics.regression_model
|
||
import openbb_terminal.econometrics.regression_view
|
||
from openbb_terminal import feature_flags as obbff
|
||
from openbb_terminal.decorators import log_start_end
|
||
from openbb_terminal.helper_funcs import (
|
||
check_positive,
|
||
check_positive_float,
|
||
NO_EXPORT,
|
||
EXPORT_ONLY_FIGURES_ALLOWED,
|
||
EXPORT_ONLY_RAW_DATA_ALLOWED,
|
||
EXPORT_BOTH_RAW_DATA_AND_FIGURES,
|
||
export_data,
|
||
)
|
||
from openbb_terminal.helper_funcs import (
|
||
print_rich_table,
|
||
check_list_values,
|
||
)
|
||
from openbb_terminal.menu import session
|
||
from openbb_terminal.parent_classes import BaseController
|
||
from openbb_terminal.rich_config import console, MenuText
|
||
from openbb_terminal.econometrics import econometrics_model, econometrics_view
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# pylint: disable=R0902
|
||
|
||
|
||
class EconometricsController(BaseController):
|
||
"""Econometrics class"""
|
||
|
||
CHOICES_COMMANDS: List[str] = [
|
||
"load",
|
||
"export",
|
||
"remove",
|
||
"plot",
|
||
"show",
|
||
"type",
|
||
"desc",
|
||
"index",
|
||
"clean",
|
||
"add",
|
||
"delete",
|
||
"combine",
|
||
"rename",
|
||
"ols",
|
||
"norm",
|
||
"root",
|
||
"panel",
|
||
"compare",
|
||
"dwat",
|
||
"bgod",
|
||
"bpag",
|
||
"granger",
|
||
"coint",
|
||
]
|
||
CHOICES_MENUS: List[str] = ["qa", "pred"]
|
||
pandas_plot_choices = [
|
||
"line",
|
||
"scatter",
|
||
"bar",
|
||
"barh",
|
||
"hist",
|
||
"box",
|
||
"kde",
|
||
"area",
|
||
"pie",
|
||
"hexbin",
|
||
]
|
||
PATH = "/econometrics/"
|
||
|
||
loaded_dataset_cols = "\n"
|
||
list_dataset_cols: List = list()
|
||
|
||
def __init__(self, queue: List[str] = None):
|
||
"""Constructor"""
|
||
super().__init__(queue)
|
||
self.files: List[str] = list()
|
||
self.datasets: Dict[str, pd.DataFrame] = dict()
|
||
self.regression: Dict[Any[Dict, Any], Any] = dict()
|
||
|
||
self.DATA_EXAMPLES: Dict[str, str] = {
|
||
"anes96": "American National Election Survey 1996",
|
||
"cancer": "Breast Cancer Data",
|
||
"ccard": "Bill Greene’s credit scoring data.",
|
||
"cancer_china": "Smoking and lung cancer in eight cities in China.",
|
||
"co2": "Mauna Loa Weekly Atmospheric CO2 Data",
|
||
"committee": "First 100 days of the US House of Representatives 1995",
|
||
"copper": "World Copper Market 1951-1975 Dataset",
|
||
"cpunish": "US Capital Punishment dataset.",
|
||
"danish_data": "Danish Money Demand Data",
|
||
"elnino": "El Nino - Sea Surface Temperatures",
|
||
"engel": "Engel (1857) food expenditure data",
|
||
"fair": "Affairs dataset",
|
||
"fertility": "World Bank Fertility Data",
|
||
"grunfeld": "Grunfeld (1950) Investment Data",
|
||
"heart": "Transplant Survival Data",
|
||
"interest_inflation": "(West) German interest and inflation rate 1972-1998",
|
||
"longley": "Longley dataset",
|
||
"macrodata": "United States Macroeconomic data",
|
||
"modechoice": "Travel Mode Choice",
|
||
"nile": "Nile River flows at Ashwan 1871-1970",
|
||
"randhie": "RAND Health Insurance Experiment Data",
|
||
"scotland": "Taxation Powers Vote for the Scottish Parliament 1997",
|
||
"spector": "Spector and Mazzeo (1980) - Program Effectiveness Data",
|
||
"stackloss": "Stack loss data",
|
||
"star98": "Star98 Educational Dataset",
|
||
"statecrim": "Statewide Crime Data 2009",
|
||
"strikes": "U.S. Strike Duration Data",
|
||
"sunspots": "Yearly sunspots data 1700-2008",
|
||
"wage_panel": "Veila and M. Verbeek (1998): Whose Wages Do Unions Raise?",
|
||
}
|
||
|
||
self.DATA_TYPES: List[str] = ["int", "float", "str", "bool", "category", "date"]
|
||
|
||
for regression in [
|
||
"OLS",
|
||
"POLS",
|
||
"BOLS",
|
||
"RE",
|
||
"FE",
|
||
"FE_EE",
|
||
"FE_IE",
|
||
"FE_EE_IE",
|
||
"FDOLS",
|
||
]:
|
||
self.regression[regression] = {
|
||
"data": {},
|
||
"independent": {},
|
||
"dependent": {},
|
||
"model": {},
|
||
}
|
||
|
||
self.signs: Dict[Any, Any] = {
|
||
"div": "/",
|
||
"mul": "*",
|
||
"add": "+",
|
||
"sub": "-",
|
||
"mod": "%",
|
||
"pow": "**",
|
||
}
|
||
self.file_types = ["csv", "xlsx"]
|
||
self.DATA_FILES = {
|
||
filepath.name: filepath
|
||
for file_type in self.file_types
|
||
for filepath in chain(
|
||
Path(obbff.EXPORT_FOLDER_PATH).rglob(f"*.{file_type}"),
|
||
Path("custom_imports").rglob(f"*.{file_type}"),
|
||
)
|
||
if filepath.is_file()
|
||
}
|
||
|
||
if session and obbff.USE_PROMPT_TOOLKIT:
|
||
choices: dict = {c: {} for c in self.controller_choices}
|
||
choices["load"] = {c: None for c in self.DATA_FILES.keys()}
|
||
choices["show"] = {c: None for c in self.files}
|
||
|
||
for feature in ["export", "show", "desc", "clear", "index"]:
|
||
choices[feature] = {c: None for c in self.files}
|
||
|
||
for feature in [
|
||
"general",
|
||
"type",
|
||
"plot",
|
||
"norm",
|
||
"root",
|
||
"granger",
|
||
"coint",
|
||
"regressions",
|
||
]:
|
||
choices[feature] = dict()
|
||
|
||
self.choices = choices
|
||
|
||
choices["support"] = self.SUPPORT_CHOICES
|
||
|
||
self.completer = NestedCompleter.from_nested_dict(choices)
|
||
|
||
def update_runtime_choices(self):
|
||
if session and obbff.USE_PROMPT_TOOLKIT:
|
||
dataset_columns = {
|
||
f"{dataset}.{column}": {column: None, dataset: None}
|
||
for dataset, dataframe in self.datasets.items()
|
||
for column in dataframe.columns
|
||
}
|
||
|
||
for feature in [
|
||
"general",
|
||
"plot",
|
||
"norm",
|
||
"root",
|
||
"coint",
|
||
"regressions",
|
||
"ols",
|
||
"panel",
|
||
"delete",
|
||
]:
|
||
self.choices[feature] = dataset_columns
|
||
for feature in [
|
||
"export",
|
||
"show",
|
||
"clean",
|
||
"index",
|
||
"remove",
|
||
"combine",
|
||
"rename",
|
||
]:
|
||
self.choices[feature] = {c: None for c in self.files}
|
||
|
||
self.choices["type"] = {
|
||
c: None for c in self.files + list(dataset_columns.keys())
|
||
}
|
||
self.choices["desc"] = {
|
||
c: None for c in self.files + list(dataset_columns.keys())
|
||
}
|
||
|
||
pairs_timeseries = list()
|
||
for dataset_col in list(dataset_columns.keys()):
|
||
pairs_timeseries += [
|
||
f"{dataset_col},{dataset_col2}"
|
||
for dataset_col2 in list(dataset_columns.keys())
|
||
if dataset_col != dataset_col2
|
||
]
|
||
|
||
self.choices["granger"] = {c: None for c in pairs_timeseries}
|
||
|
||
self.completer = NestedCompleter.from_nested_dict(self.choices)
|
||
|
||
def print_help(self):
|
||
"""Print help"""
|
||
mt = MenuText("econometrics/")
|
||
mt.add_param(
|
||
"_data_loc",
|
||
f"\n\t{obbff.EXPORT_FOLDER_PATH}\n\t{Path('custom_imports').resolve()}",
|
||
)
|
||
mt.add_raw("\n")
|
||
mt.add_cmd("load")
|
||
mt.add_cmd("remove", "", self.files)
|
||
mt.add_raw("\n")
|
||
mt.add_param("_loaded", self.loaded_dataset_cols)
|
||
|
||
mt.add_info("_exploration_")
|
||
mt.add_cmd("show", "", self.files)
|
||
mt.add_cmd("plot", "", self.files)
|
||
mt.add_cmd("type", "", self.files)
|
||
mt.add_cmd("desc", "", self.files)
|
||
mt.add_cmd("index", "", self.files)
|
||
mt.add_cmd("clean", "", self.files)
|
||
mt.add_cmd("add", "", self.files)
|
||
mt.add_cmd("delete", "", self.files)
|
||
mt.add_cmd("combine", "", self.files)
|
||
mt.add_cmd("rename", "", self.files)
|
||
mt.add_cmd("export", "", self.files)
|
||
mt.add_info("_tests_")
|
||
mt.add_cmd("norm", "", self.files)
|
||
mt.add_cmd("root", "", self.files)
|
||
mt.add_cmd("granger", "", self.files)
|
||
mt.add_cmd("coint", "", self.files)
|
||
mt.add_info("_regression_")
|
||
mt.add_cmd("ols", "", self.files)
|
||
mt.add_cmd("panel", "", self.files)
|
||
mt.add_cmd("compare", "", self.files)
|
||
mt.add_info("_regression_tests_")
|
||
mt.add_cmd("dwat", "", self.files and self.regression["OLS"]["model"])
|
||
mt.add_cmd("bgod", "", self.files and self.regression["OLS"]["model"])
|
||
mt.add_cmd("bpag", "", self.files and self.regression["OLS"]["model"])
|
||
|
||
console.print(text=mt.menu_text, menu="Econometrics")
|
||
|
||
def custom_reset(self):
|
||
"""Class specific component of reset command"""
|
||
if self.files:
|
||
load_files = [f"load {file}" for file in self.files]
|
||
return ["econometrics"] + load_files
|
||
return []
|
||
|
||
@log_start_end(log=logger)
|
||
def call_load(self, other_args: List[str]):
|
||
"""Process load"""
|
||
parser = argparse.ArgumentParser(
|
||
add_help=False,
|
||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||
prog="load",
|
||
description="Load custom dataset (from previous export, custom imports or StatsModels).",
|
||
)
|
||
parser.add_argument(
|
||
"-f",
|
||
"--file",
|
||
help="File to load data in (can be custom import, may have been exported before or can be from Statsmodels)",
|
||
type=str,
|
||
)
|
||
parser.add_argument(
|
||
"-a",
|
||
"--alias",
|
||
help="Alias name to give to the dataset",
|
||
type=str,
|
||
)
|
||
|
||
parser.add_argument(
|
||
"-e",
|
||
"--examples",
|
||
help="Use this argument to show examples of Statsmodels to load in. "
|
||
"See: https://www.statsmodels.org/devel/datasets/index.html",
|
||
action="store_true",
|
||
default=False,
|
||
dest="examples",
|
||
)
|
||
|
||
if other_args and "-" not in other_args[0][0]:
|
||
other_args.insert(0, "-f")
|
||
ns_parser = self.parse_known_args_and_warn(parser, other_args)
|
||
|
||
if ns_parser:
|
||
# show examples from statsmodels
|
||
if ns_parser.examples:
|
||
df = pd.DataFrame.from_dict(self.DATA_EXAMPLES, orient="index")
|
||
print_rich_table(
|
||
df,
|
||
headers=list(["description"]),
|
||
show_index=True,
|
||
index_name="file name",
|
||
title="Examples from Statsmodels",
|
||
)
|
||
return
|
||
|
||
if ns_parser.file:
|
||
possible_data = list(self.DATA_EXAMPLES.keys()) + list(
|
||
self.DATA_FILES.keys()
|
||
)
|
||
if ns_parser.file not in possible_data:
|
||
file = ""
|
||
# Try to see if the user is just missing the extension
|
||
for file_ext in list(self.DATA_FILES.keys()):
|
||
if file_ext.startswith(ns_parser.file):
|
||
# found the correct file
|
||
file = file_ext
|
||
break
|
||
|
||
if not file:
|
||
console.print(
|
||
"[red]The file/dataset selected does not exist.[/red]\n"
|
||
)
|
||
return
|
||
else:
|
||
file = ns_parser.file
|
||
|
||
if ns_parser.alias:
|
||
alias = ns_parser.alias
|
||
else:
|
||
if "." in ns_parser.file:
|
||
alias = ".".join(ns_parser.file.split(".")[:-1])
|
||
else:
|
||
alias = ns_parser.file
|
||
|
||
# check if this dataset has been added already
|
||
if alias in self.files:
|
||
console.print(
|
||
"[red]The file/dataset selected has already been loaded.[/red]\n"
|
||
)
|
||
return
|
||
|
||
data = econometrics_model.load(
|
||
file, self.file_types, self.DATA_FILES, self.DATA_EXAMPLES
|
||
)
|
||
|
||
if not data.empty:
|
||
data.columns = data.columns.map(
|
||
lambda x: x.lower().replace(" ", "_")
|
||
)
|
||
|
||
self.files.append(alias)
|
||
self.datasets[alias] = data
|
||
|
||
self.update_runtime_choices()
|
||
|
||
# Process new datasets to be updated
|
||
self.list_dataset_cols = list()
|
||
maxfile = max(len(file) for file in self.files)
|
||
self.loaded_dataset_cols = "\n"
|
||
for dataset, data in self.datasets.items():
|
||
self.loaded_dataset_cols += (
|
||
f" {dataset} {(maxfile - len(dataset)) * ' '}: "
|
||
f"{', '.join(data.columns)}\n"
|
||
)
|
||
|
||
for col in data.columns:
|
||
self.list_dataset_cols.append(f"{dataset}.{col}")
|
||
|
||
console.print()
|
||
|
||
@log_start_end(log=logger)
|
||
def call_export(self, other_args: List[str]):
|
||
"""Process export command"""
|
||
parser = argparse.ArgumentParser(
|
||
add_help=False,
|
||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||
prog="export",
|
||
description="Export dataset to Excel",
|
||
)
|
||
|
||
parser.add_argument(
|
||
"-n",
|
||
"--name",
|
||
dest="name",
|
||
help="The name of the dataset you wish to export",
|
||
type=str,
|
||
)
|
||
|
||
parser.add_argument(
|
||
"-t",
|
||
"--type",
|
||
help="The file type you wish to export to",
|
||
dest="type",
|
||
choices=self.file_types,
|
||
type=str,
|
||
default="xlsx",
|
||
)
|
||
|
||
if other_args and "-" not in other_args[0][0]:
|
||
other_args.insert(0, "-n")
|
||
ns_parser = self.parse_known_args_and_warn(
|
||
parser, other_args, export_allowed=NO_EXPORT
|
||
)
|
||
|
||
if ns_parser:
|
||
if not ns_parser.name or ns_parser.name not in self.datasets:
|
||
console.print("Please enter a valid dataset.")
|
||
else:
|
||
export_data(
|
||
ns_parser.type,
|
||
os.path.dirname(os.path.abspath(__file__)),
|
||
ns_parser.name,
|
||
self.datasets[ns_parser.name],
|
||
)
|
||
|
||
console.print()
|
||
|
||
@log_start_end(log=logger)
|
||
def call_remove(self, other_args: List[str]):
|
||
"""Process clear"""
|
||
parser = argparse.ArgumentParser(
|
||
add_help=False,
|
||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||
prog="remove",
|
||
description="Remove a dataset from the loaded dataset list",
|
||
)
|
||
parser.add_argument(
|
||
"-n",
|
||
"--name",
|
||
help="The name of the dataset you want to remove",
|
||
dest="name",
|
||
type=str,
|
||
choices=list(self.datasets.keys()),
|
||
)
|
||
|
||
if other_args and "-" not in other_args[0][0]:
|
||
other_args.insert(0, "-n")
|
||
ns_parser = self.parse_known_args_and_warn(parser, other_args, NO_EXPORT)
|
||
|
||
if not ns_parser.name:
|
||
console.print("Please enter a valid dataset.\n")
|
||
return
|
||
|
||
if ns_parser.name not in self.datasets:
|
||
console.print(f"[red]'{ns_parser.name}' is not a loaded dataset.[/red]\n")
|
||
return
|
||
|
||
del self.datasets[ns_parser.name]
|
||
self.files.remove(ns_parser.name)
|
||
|
||
self.update_runtime_choices()
|
||
|
||
# Process new datasets to be updated
|
||
self.list_dataset_cols = list()
|
||
maxfile = max(len(file) for file in self.files)
|
||
self.loaded_dataset_cols = "\n"
|
||
for dataset, data in self.datasets.items():
|
||
self.loaded_dataset_cols += f"\t{dataset} {(maxfile - len(dataset)) * ' '}: {', '.join(data.columns)}\n"
|
||
|
||
for col in data.columns:
|
||
self.list_dataset_cols.append(f"{dataset}.{col}")
|
||
|
||
@log_start_end(log=logger)
|
||
def call_plot(self, other_args: List[str]):
|
||
"""Process plot command"""
|
||
parser = argparse.ArgumentParser(
|
||
add_help=False,
|
||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||
prog="plot",
|
||
description="Plot data based on the index",
|
||
)
|
||
parser.add_argument(
|
||
"-v",
|
||
"--values",
|
||
help="Dataset.column values to be displayed in a plot",
|
||
dest="values",
|
||
type=check_list_values(self.choices["plot"]),
|
||
)
|
||
|
||
if other_args and "-" not in other_args[0][0]:
|
||
other_args.insert(0, "-v")
|
||
ns_parser = self.parse_known_args_and_warn(
|
||
parser, other_args, export_allowed=EXPORT_ONLY_FIGURES_ALLOWED
|
||
)
|
||
|
||
if ns_parser and ns_parser.values:
|
||
data: Dict = {}
|
||
for datasetcol in ns_parser.values:
|
||
dataset, col = datasetcol.split(".")
|
||
data[datasetcol] = self.datasets[dataset][col]
|
||
|
||
econometrics_view.display_plot(
|
||
data,
|
||
ns_parser.export,
|
||
)
|
||
|
||
@log_start_end(log=logger)
|
||
def call_show(self, other_args: List[str]):
|
||
"""Process show command"""
|
||
parser = argparse.ArgumentParser(
|
||
add_help=False,
|
||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||
prog="show",
|
||
description="Show a portion of the DataFrame",
|
||
)
|
||
|
||
parser.add_argument(
|
||
"-n",
|
||
"--name",
|
||
type=str,
|
||
choices=self.files,
|
||
dest="name",
|
||
help="The name of the database you want to show data for",
|
||
)
|
||
|
||
parser.add_argument(
|
||
"-s",
|
||
"--sortcol",
|
||
help="Sort based on a column in the DataFrame",
|
||
nargs="+",
|
||
type=str,
|
||
dest="sortcol",
|
||
default="",
|
||
)
|
||
parser.add_argument(
|
||
"-a",
|
||
"--ascend",
|
||
help="Use this argument to sort in a descending order",
|
||
action="store_true",
|
||
default=False,
|
||
dest="ascend",
|
||
)
|
||
|
||
if other_args and "-" not in other_args[0][0]:
|
||
other_args.insert(0, "-n")
|
||
ns_parser = self.parse_known_args_and_warn(
|
||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED, limit=10
|
||
)
|
||
|
||
if ns_parser:
|
||
if not ns_parser.name:
|
||
dataset_names = list(self.datasets.keys())
|
||
else:
|
||
dataset_names = [ns_parser.name]
|
||
|
||
for name in dataset_names:
|
||
df = self.datasets[name]
|
||
|
||
if name in self.datasets and self.datasets[name].empty:
|
||
return console.print(
|
||
f"[red]No data available for {ns_parser.name}.[/red]\n"
|
||
)
|
||
if ns_parser.sortcol:
|
||
sort_column = " ".join(ns_parser.sortcol)
|
||
if sort_column not in self.datasets[name].columns:
|
||
console.print(
|
||
f"[red]{sort_column} not a valid column. Showing without sorting.\n[/red]"
|
||
)
|
||
else:
|
||
df = df.sort_values(by=sort_column, ascending=ns_parser.ascend)
|
||
|
||
print_rich_table(
|
||
df.head(ns_parser.limit),
|
||
headers=list(df.columns),
|
||
show_index=True,
|
||
title=f"Dataset {name} | Showing {ns_parser.limit} of {len(df)} rows",
|
||
)
|
||
|
||
export_data(
|
||
ns_parser.export,
|
||
os.path.dirname(os.path.abspath(__file__)),
|
||
f"{ns_parser.name}_show",
|
||
df.head(ns_parser.limit),
|
||
)
|
||
|
||
@log_start_end(log=logger)
|
||
def call_desc(self, other_args: List[str]):
|
||
"""Process desc command"""
|
||
parser = argparse.ArgumentParser(
|
||
add_help=False,
|
||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||
prog="desc",
|
||
description="Show the descriptive statistics of the dataset",
|
||
)
|
||
parser.add_argument(
|
||
"-n",
|
||
"--name",
|
||
type=str,
|
||
choices=self.choices["desc"],
|
||
dest="name",
|
||
help="The name of the dataset.column you want to show the descriptive statistics",
|
||
required="-h" not in other_args,
|
||
)
|
||
if other_args and "-" not in other_args[0][0]:
|
||
other_args.insert(0, "-n")
|
||
ns_parser = self.parse_known_args_and_warn(
|
||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||
)
|
||
|
||
if ns_parser:
|
||
if "." in ns_parser.name:
|
||
dataset, col = ns_parser.name.split(".")
|
||
|
||
df = self.datasets[dataset][col].describe()
|
||
print_rich_table(
|
||
df.to_frame(),
|
||
headers=[col],
|
||
show_index=True,
|
||
title=f"Statistics for dataset: '{dataset}'",
|
||
)
|
||
|
||
export_data(
|
||
ns_parser.export,
|
||
os.path.dirname(os.path.abspath(__file__)),
|
||
f"{dataset}_{col}_desc",
|
||
df,
|
||
)
|
||
else:
|
||
df = self.datasets[ns_parser.name].describe()
|
||
print_rich_table(
|
||
df,
|
||
headers=self.datasets[ns_parser.name].columns,
|
||
show_index=True,
|
||
title=f"Statistics for dataset: '{ns_parser.name}'",
|
||
)
|
||
|
||
export_data(
|
||
ns_parser.export,
|
||
os.path.dirname(os.path.abspath(__file__)),
|
||
f"{ns_parser.name}_desc",
|
||
df,
|
||
)
|
||
|
||
@log_start_end(log=logger)
|
||
def call_type(self, other_args: List[str]):
|
||
"""Process type"""
|
||
parser = argparse.ArgumentParser(
|
||
add_help=False,
|
||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||
prog="type",
|
||
description="Show the type of the columns of the dataset and/or change the type of the column",
|
||
)
|
||
parser.add_argument(
|
||
"-n",
|
||
"--name",
|
||
type=str,
|
||
dest="name",
|
||
help="Provide dataset.column series to change type or dataset to see types.",
|
||
choices=self.choices["type"],
|
||
)
|
||
parser.add_argument(
|
||
"-f",
|
||
"--format",
|
||
type=str,
|
||
choices=self.DATA_TYPES,
|
||
dest="format",
|
||
help=(
|
||
"Set the format for the dataset.column defined. This can be: "
|
||
"date, int, float, str, bool or category"
|
||
),
|
||
)
|
||
|
||
if other_args and "-" not in other_args[0][0]:
|
||
other_args.insert(0, "-n")
|
||
ns_parser = self.parse_known_args_and_warn(parser, other_args, NO_EXPORT)
|
||
|
||
if ns_parser:
|
||
if ns_parser.name:
|
||
if "." in ns_parser.name:
|
||
dataset, column = ns_parser.name.split(".")
|
||
if ns_parser.format:
|
||
if ns_parser.format == "date":
|
||
self.datasets[dataset][column] = pd.to_datetime(
|
||
self.datasets[dataset][column].values,
|
||
)
|
||
else:
|
||
self.datasets[dataset][column] = self.datasets[dataset][
|
||
column
|
||
].astype(ns_parser.format)
|
||
|
||
console.print(
|
||
f"Update '{ns_parser.name}' with type '{ns_parser.format}'"
|
||
)
|
||
else:
|
||
console.print(
|
||
f"The type of '{ns_parser.name}' is '{self.datasets[dataset][column].dtypes}'"
|
||
)
|
||
|
||
else:
|
||
print_rich_table(
|
||
pd.DataFrame(self.datasets[ns_parser.name].dtypes),
|
||
headers=list(["dtype"]),
|
||
show_index=True,
|
||
index_name="column",
|
||
title=str(ns_parser.name),
|
||
)
|
||
else:
|
||
for dataset_name, data in self.datasets.items():
|
||
print_rich_table(
|
||
pd.DataFrame(data.dtypes),
|
||
headers=list(["dtype"]),
|
||
show_index=True,
|
||
index_name="column",
|
||
title=str(dataset_name),
|
||
)
|
||
|
||
console.print()
|
||
|
||
@log_start_end(log=logger)
|
||
def call_index(self, other_args: List[str]):
|
||
"""Process index"""
|
||
parser = argparse.ArgumentParser(
|
||
add_help=False,
|
||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||
prog="index",
|
||
description="Set a (multi) index for the dataset",
|
||
)
|
||
parser.add_argument(
|
||
"-n",
|
||
"--name",
|
||
type=str,
|
||
dest="name",
|
||
choices=list(self.datasets.keys()),
|
||
help="Name of dataset to select index from",
|
||
required="-h" not in other_args,
|
||
)
|
||
parser.add_argument(
|
||
"-i",
|
||
"--index",
|
||
type=str,
|
||
dest="index",
|
||
help="Columns from the dataset the user wishes to set as default",
|
||
default="",
|
||
)
|
||
parser.add_argument(
|
||
"-a",
|
||
"--adjustment",
|
||
help="Whether to allow for making adjustments to the dataset to align it with the use case for "
|
||
"Timeseries and Panel Data regressions",
|
||
dest="adjustment",
|
||
action="store_true",
|
||
default=False,
|
||
)
|
||
parser.add_argument(
|
||
"-d",
|
||
"--drop",
|
||
help="Whether to drop the column(s) the index is set for.",
|
||
dest="drop",
|
||
action="store_true",
|
||
default=False,
|
||
)
|
||
if other_args and "-" not in other_args[0][0]:
|
||
other_args.insert(0, "-n")
|
||
ns_parser = self.parse_known_args_and_warn(parser, other_args, NO_EXPORT)
|
||
|
||
if ns_parser:
|
||
name = ns_parser.name
|
||
index = ns_parser.index
|
||
|
||
if index:
|
||
if "," in index:
|
||
values_found = [val.strip() for val in index.split(",")]
|
||
else:
|
||
values_found = [index]
|
||
|
||
columns = list()
|
||
for value in values_found:
|
||
# check if the value is valid
|
||
if value in self.datasets[name].columns:
|
||
columns.append(value)
|
||
else:
|
||
console.print(f"[red]'{value}' is not valid.[/red]")
|
||
|
||
dataset = self.datasets[name]
|
||
|
||
if not pd.Index(np.arange(0, len(dataset))).equals(dataset.index):
|
||
console.print(
|
||
"As an index has been set, resetting the current index."
|
||
)
|
||
if dataset.index.name in dataset.columns:
|
||
dataset = dataset.reset_index(drop=True)
|
||
else:
|
||
dataset = dataset.reset_index(drop=False)
|
||
|
||
for column in columns:
|
||
if column not in dataset.columns:
|
||
console.print(
|
||
f"[red]The column '{column}' is not available in the dataset {name}."
|
||
f"Please choose one of the following: {', '.join(dataset.columns)}[/red]"
|
||
)
|
||
return
|
||
|
||
if ns_parser.adjustment:
|
||
if len(columns) > 1 and dataset[columns[0]].isnull().any():
|
||
null_values = dataset[dataset[columns[0]].isnull()]
|
||
console.print(
|
||
f"The column '{columns[0]}' contains {len(null_values)} NaN values. As multiple columns are "
|
||
f"provided, it is assumed this column represents entities (i), the NaN values are "
|
||
f"forward filled. Remove the -a argument to disable this."
|
||
)
|
||
dataset[columns[0]] = dataset[columns[0]].fillna(method="ffill")
|
||
if dataset[columns[-1]].isnull().any():
|
||
# This checks whether NaT (missing values) exists within the DataFrame
|
||
null_values = dataset[dataset[columns[-1]].isnull()]
|
||
console.print(
|
||
f"The time index '{columns[-1]}' contains {len(null_values)} "
|
||
"NaNs which are removed from the dataset. Remove the -a argument to disable this."
|
||
)
|
||
dataset = dataset[dataset[columns[-1]].notnull()]
|
||
|
||
self.datasets[name] = dataset.set_index(columns, drop=ns_parser.drop)
|
||
console.print(
|
||
f"Successfully updated '{name}' index to be '{', '.join(columns)}'\n"
|
||
)
|
||
|
||
self.update_runtime_choices()
|
||
else:
|
||
print_rich_table(
|
||
self.datasets[name].head(3),
|
||
headers=list(self.datasets[name].columns),
|
||
show_index=True,
|
||
title=f"Dataset '{name}'",
|
||
)
|
||
|
||
@log_start_end(log=logger)
|
||
def call_clean(self, other_args: List[str]):
|
||
"""Process clean"""
|
||
parser = argparse.ArgumentParser(
|
||
add_help=False,
|
||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||
prog="clean",
|
||
description="Clean a dataset by filling and dropping NaN values.",
|
||
)
|
||
parser.add_argument(
|
||
"-n",
|
||
"--name",
|
||
help="The name of the dataset you want to clean up",
|
||
dest="name",
|
||
type=str,
|
||
choices=list(self.datasets.keys()),
|
||
)
|
||
parser.add_argument(
|
||
"-f",
|
||
"--fill",
|
||
help="The method of filling NaNs. This has options to fill rows (rfill, rbfill, rffill) or fill "
|
||
"columns (cfill, cbfill, cffill). Furthermore, it has the option to forward fill and backward fill "
|
||
"(up to --limit) which refer to how many rows/columns can be set equal to the last non-NaN value",
|
||
dest="fill",
|
||
choices=["rfill", "cfill", "rbfill", "cbfill", "rffill", "bffill"],
|
||
default="",
|
||
)
|
||
parser.add_argument(
|
||
"-d",
|
||
"--drop",
|
||
help="The method of dropping NaNs. This either has the option rdrop (drop rows that contain NaNs) "
|
||
"or cdrop (drop columns that contain NaNs)",
|
||
dest="drop",
|
||
choices=["rdrop", "cdrop"],
|
||
default="",
|
||
)
|
||
if other_args and "-" not in other_args[0][0]:
|
||
other_args.insert(0, "-n")
|
||
ns_parser = self.parse_known_args_and_warn(
|
||
parser, other_args, NO_EXPORT, limit=5
|
||
)
|
||
if ns_parser:
|
||
self.datasets[ns_parser.name] = econometrics_model.clean(
|
||
self.datasets[ns_parser.name],
|
||
ns_parser.fill,
|
||
ns_parser.drop,
|
||
ns_parser.limit,
|
||
)
|
||
console.print(f"Successfully cleaned '{ns_parser.name}' dataset")
|
||
console.print()
|
||
|
||
@log_start_end(log=logger)
|
||
def call_add(self, other_args: List[str]):
|
||
"""Process add"""
|
||
parser = argparse.ArgumentParser(
|
||
add_help=False,
|
||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||
prog="add",
|
||
description="Add columns to your dataframe with the option to use formulas. E.g."
|
||
" newdatasetcol = basedatasetcol sign criteriaordatasetcol"
|
||
" thesis.high_revenue = thesis.revenue > 1000"
|
||
" dataset.debt_ratio = dataset.debt div dataset2.assets",
|
||
)
|
||
parser.add_argument(
|
||
"-n",
|
||
"--newdatasetcol",
|
||
help="New dataset column to be added with format: dataset.column",
|
||
dest="newdatasetcol",
|
||
type=str,
|
||
required="-h" not in other_args,
|
||
)
|
||
parser.add_argument(
|
||
"-b",
|
||
"--basedatasetcol",
|
||
help="Base dataset column to be used as base with format: dataset.column",
|
||
dest="basedatasetcol",
|
||
type=str,
|
||
required="-h" not in other_args,
|
||
)
|
||
parser.add_argument(
|
||
"-s",
|
||
"--sign",
|
||
help="Sign to be applied to the base dataset column",
|
||
dest="sign",
|
||
choices=list(self.signs.keys()) + [">", "<", ">=", "<=", "=="],
|
||
required="-h" not in other_args,
|
||
type=str,
|
||
)
|
||
parser.add_argument(
|
||
"-c",
|
||
"--criteriaordatasetcol",
|
||
help="Either dataset column to be applied on top of base dataset or criteria",
|
||
dest="criteriaordatasetcol",
|
||
required="-h" not in other_args,
|
||
type=str,
|
||
)
|
||
if other_args and "-" not in other_args[0][0]:
|
||
other_args.insert(0, "-n")
|
||
ns_parser = self.parse_known_args_and_warn(parser, other_args, NO_EXPORT)
|
||
|
||
if ns_parser:
|
||
dataset, new_column = ns_parser.newdatasetcol.split(".")
|
||
dataset2, existing_column = ns_parser.basedatasetcol.split(".")
|
||
|
||
for sign, operator in self.signs.items():
|
||
if sign == ns_parser.sign:
|
||
ns_parser.sign = operator
|
||
|
||
if dataset not in self.datasets:
|
||
console.print(
|
||
f"Not able to find the dataset {dataset}. Please choose one of "
|
||
f"the following: {', '.join(self.datasets)}"
|
||
)
|
||
elif dataset2 not in self.datasets:
|
||
console.print(
|
||
f"Not able to find the dataset {dataset2}. Please choose one of "
|
||
f"the following: {', '.join(self.datasets)}"
|
||
)
|
||
elif existing_column not in self.datasets[dataset2]:
|
||
console.print(
|
||
f"Not able to find the column {existing_column}. Please choose one of "
|
||
f"the following: {', '.join(self.datasets[dataset2].columns)}"
|
||
)
|
||
elif len(ns_parser.criteriaordatasetcol.split(".")) > 1:
|
||
dataset3, existing_column2 = ns_parser.criteriaordatasetcol.split(".")
|
||
|
||
if dataset3 not in self.datasets:
|
||
console.print(
|
||
f"Not able to find the dataset {dataset3}. Please choose one of "
|
||
f"the following: {', '.join(self.datasets)}"
|
||
)
|
||
|
||
elif existing_column2 not in self.datasets[dataset3]:
|
||
console.print(
|
||
f"Not able to find the column {existing_column2}. Please choose one of "
|
||
f"the following: {', '.join(self.datasets[dataset3].columns)}"
|
||
)
|
||
else:
|
||
pd.eval(
|
||
f"{new_column} = self.datasets[dataset2][existing_column] "
|
||
f"{ns_parser.sign} self.datasets[dataset3][existing_column2]",
|
||
target=self.datasets[dataset],
|
||
inplace=True,
|
||
)
|
||
else:
|
||
pd.eval(
|
||
f"{new_column} = self.datasets[dataset2][existing_column] "
|
||
f"{ns_parser.sign} {ns_parser.criteriaordatasetcol}",
|
||
target=self.datasets[dataset],
|
||
inplace=True,
|
||
)
|
||
|
||
self.update_runtime_choices()
|
||
console.print()
|
||
|
||
@log_start_end(log=logger)
|
||
def call_delete(self, other_args: List[str]):
|
||
"""Process add"""
|
||
parser = argparse.ArgumentParser(
|
||
add_help=False,
|
||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||
prog="delete",
|
||
description="The column you want to delete from a dataset.",
|
||
)
|
||
parser.add_argument(
|
||
"-d",
|
||
"--delete",
|
||
help="The columns you want to delete from a dataset. Use format: <dataset.column> or"
|
||
" multiple with <dataset.column>,<datasetb.column2>",
|
||
dest="delete",
|
||
type=check_list_values(self.choices["delete"]),
|
||
)
|
||
if other_args and "-" not in other_args[0][0]:
|
||
other_args.insert(0, "-d")
|
||
ns_parser = self.parse_known_args_and_warn(parser, other_args, NO_EXPORT)
|
||
|
||
if ns_parser:
|
||
for option in ns_parser.delete:
|
||
dataset, column = option.split(".")
|
||
|
||
if dataset not in self.datasets:
|
||
console.print(
|
||
f"Not able to find the dataset {dataset}. Please choose one of "
|
||
f"the following: {', '.join(self.datasets)}"
|
||
)
|
||
elif column not in self.datasets[dataset]:
|
||
console.print(
|
||
f"Not able to find the column {column}. Please choose one of "
|
||
f"the following: {', '.join(self.datasets[dataset].columns)}"
|
||
)
|
||
else:
|
||
del self.datasets[dataset][column]
|
||
|
||
self.update_runtime_choices()
|
||
console.print()
|
||
|
||
@log_start_end(log=logger)
|
||
def call_combine(self, other_args: List[str]):
|
||
"""Process combine"""
|
||
parser = argparse.ArgumentParser(
|
||
add_help=False,
|
||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||
prog="combine",
|
||
description="The columns you want to add to a dataset. The first argument is the dataset to add columns in"
|
||
"and the remaining could be: <datasetX.column2>,<datasetY.column3>",
|
||
)
|
||
parser.add_argument(
|
||
"-d",
|
||
"--dataset",
|
||
help="Dataset to add columns to",
|
||
dest="dataset",
|
||
choices=self.choices["combine"],
|
||
)
|
||
parser.add_argument(
|
||
"-c",
|
||
"--columns",
|
||
help="The columns we want to add <dataset.column>,<datasetb.column2>",
|
||
dest="columns",
|
||
type=check_list_values(self.choices["delete"]),
|
||
)
|
||
if other_args and "-" not in other_args[0][0]:
|
||
other_args.insert(0, "-d")
|
||
ns_parser = self.parse_known_args_and_warn(parser, other_args, NO_EXPORT)
|
||
|
||
if ns_parser:
|
||
if ns_parser.dataset not in self.datasets:
|
||
console.print(
|
||
f"Not able to find the dataset {ns_parser.dataset}. Please choose one of "
|
||
f"the following: {', '.join(self.datasets)}"
|
||
)
|
||
return
|
||
|
||
data = self.datasets[ns_parser.dataset]
|
||
|
||
for option in ns_parser.columns:
|
||
dataset, column = option.split(".")
|
||
|
||
if dataset not in self.datasets:
|
||
console.print(
|
||
f"Not able to find the dataset {dataset}. Please choose one of "
|
||
f"the following: {', '.join(self.datasets)}"
|
||
)
|
||
elif column not in self.datasets[dataset]:
|
||
console.print(
|
||
f"Not able to find the column {column}. Please choose one of "
|
||
f"the following: {', '.join(self.datasets[dataset].columns)}"
|
||
)
|
||
else:
|
||
data[f"{dataset}_{column}"] = self.datasets[dataset][column]
|
||
|
||
self.update_runtime_choices()
|
||
|
||
console.print()
|
||
|
||
@log_start_end(log=logger)
|
||
def call_rename(self, other_args: List[str]):
|
||
"""Process rename"""
|
||
parser = argparse.ArgumentParser(
|
||
add_help=False,
|
||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||
prog="rename",
|
||
description="The column you want to rename from a dataset.",
|
||
)
|
||
parser.add_argument(
|
||
"-d",
|
||
"--dataset",
|
||
help="Dataset that will get a column renamed",
|
||
dest="dataset",
|
||
choices=self.choices["rename"],
|
||
type=str,
|
||
)
|
||
parser.add_argument(
|
||
"-o",
|
||
"--oldcol",
|
||
help="Old column from dataset to be renamed",
|
||
dest="oldcol",
|
||
type=str,
|
||
required="-h" not in other_args,
|
||
)
|
||
parser.add_argument(
|
||
"-n",
|
||
"--newcol",
|
||
help="New column from dataset to be renamed",
|
||
dest="newcol",
|
||
type=str,
|
||
required="-h" not in other_args,
|
||
)
|
||
if other_args and "-" not in other_args[0][0]:
|
||
other_args.insert(0, "-d")
|
||
ns_parser = self.parse_known_args_and_warn(parser, other_args, NO_EXPORT)
|
||
|
||
if ns_parser:
|
||
dataset = ns_parser.dataset
|
||
column_old = ns_parser.oldcol
|
||
column_new = ns_parser.newcol
|
||
|
||
if dataset not in self.datasets:
|
||
console.print(
|
||
f"Not able to find the dataset {dataset}. Please choose one of "
|
||
f"the following: {', '.join(self.datasets)}"
|
||
)
|
||
elif column_old not in self.datasets[dataset]:
|
||
console.print(
|
||
f"Not able to find the column {column_old}. Please choose one of "
|
||
f"the following: {', '.join(self.datasets[dataset].columns)}"
|
||
)
|
||
else:
|
||
self.datasets[dataset] = self.datasets[dataset].rename(
|
||
columns={column_old: column_new}
|
||
)
|
||
|
||
self.update_runtime_choices()
|
||
|
||
console.print()
|
||
|
||
@log_start_end(log=logger)
|
||
def call_ols(self, other_args: List[str]):
|
||
"""Process ols command"""
|
||
parser = argparse.ArgumentParser(
|
||
add_help=False,
|
||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||
prog="ols",
|
||
description="Performs an OLS regression on timeseries data.",
|
||
)
|
||
parser.add_argument(
|
||
"-d",
|
||
"--dependent",
|
||
type=str,
|
||
dest="dependent",
|
||
help="The dependent variable on the regression you would like to perform",
|
||
required="-h" not in other_args,
|
||
)
|
||
parser.add_argument(
|
||
"-i",
|
||
"--independent",
|
||
type=check_list_values(self.choices["regressions"]),
|
||
dest="independent",
|
||
help=(
|
||
"The independent variables on the regression you would like to perform. "
|
||
"E.g. historical.high,historical.low"
|
||
),
|
||
required="-h" not in other_args,
|
||
)
|
||
|
||
if other_args and "-" not in other_args[0][0]:
|
||
other_args.insert(0, "-d")
|
||
ns_parser = self.parse_known_args_and_warn(
|
||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||
)
|
||
if ns_parser:
|
||
if "," in ns_parser.dependent:
|
||
console.print(
|
||
"It appears you have selected multiple variables for the dependent variable. "
|
||
"The model only accepts one.\nDid you intend to include these variables as independent "
|
||
f"variables? Use -i {ns_parser.dependent} in this case.\n"
|
||
)
|
||
elif ns_parser.dependent in self.choices["regressions"]:
|
||
(
|
||
self.regression["OLS"]["data"],
|
||
self.regression["OLS"]["dependent"],
|
||
self.regression["OLS"]["independent"],
|
||
self.regression["OLS"]["model"],
|
||
) = openbb_terminal.econometrics.regression_model.get_ols(
|
||
[ns_parser.dependent] + ns_parser.independent,
|
||
self.datasets,
|
||
self.choices["regressions"],
|
||
export=ns_parser.export,
|
||
)
|
||
else:
|
||
console.print(
|
||
f"{ns_parser.dependent} not in {','.join(self.choices['regressions'])}\n"
|
||
f"Please choose a valid dataset and column combination.\n"
|
||
)
|
||
|
||
@log_start_end(log=logger)
|
||
def call_norm(self, other_args: List[str]):
|
||
"""Process normality command"""
|
||
parser = argparse.ArgumentParser(
|
||
add_help=False,
|
||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||
prog="norm",
|
||
description="Test whether the used data is normally distributed.",
|
||
)
|
||
parser.add_argument(
|
||
"-v",
|
||
"--value",
|
||
type=str,
|
||
choices=self.choices["norm"],
|
||
dest="column",
|
||
help="The dataset.column you want to test normality for",
|
||
required="-h" not in other_args,
|
||
)
|
||
parser.add_argument(
|
||
"-p",
|
||
"--plot",
|
||
dest="plot",
|
||
help="Whether you wish to plot a histogram to visually depict normality",
|
||
action="store_true",
|
||
default=False,
|
||
)
|
||
|
||
if other_args and "-" not in other_args[0][0]:
|
||
other_args.insert(0, "-v")
|
||
ns_parser = self.parse_known_args_and_warn(
|
||
parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||
)
|
||
|
||
if ns_parser and ns_parser.column:
|
||
column, dataset = self.choices["norm"][ns_parser.column].keys()
|
||
|
||
if isinstance(self.datasets[dataset][column].index, pd.MultiIndex):
|
||
return console.print(
|
||
f"The column '{column}' from the dataset '{dataset}' is a MultiIndex. To test for normality in a "
|
||
"timeseries, make sure to set a singular time index.\n"
|
||
)
|
||
|
||
if dataset in self.datasets:
|
||
if isinstance(self.datasets[dataset], pd.Series):
|
||
data = self.datasets[dataset]
|
||
elif isinstance(self.datasets[dataset], pd.DataFrame):
|
||
data = self.datasets[dataset][column]
|
||
else:
|
||
return console.print(
|
||
f"The type of {dataset} ({type(dataset)} is not an option."
|
||
)
|
||
else:
|
||
return console.print(f"Can not find {dataset}. Did you load the data?")
|
||
|
||
return econometrics_view.display_norm(
|
||
data, dataset, column, ns_parser.plot, ns_parser.export
|
||
)
|
||
|
||
@log_start_end(log=logger)
|
||
def call_root(self, other_args: List[str]):
|
||
"""Process unit root command"""
|
||
parser = argparse.ArgumentParser(
|
||
add_help=False,
|
||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||
prog="root",
|
||
description="Show unit root tests of a column of a dataset",
|
||
)
|
||
parser.add_argument(
|
||
"-v",
|
||
"--value",
|
||
type=str,
|
||
choices=self.choices["root"],
|
||
dest="column",
|
||
help="The column and name of the database you want test unit root for",
|
||
required="-h" not in other_args,
|
||
)
|
||
|
||
parser.add_argument(
|
||
"-r",
|
||
"--fuller_reg",
|
||
help="Type of regression. Can be 'c','ct','ctt','nc'. c - Constant and t - trend order",
|
||
choices=["c", "ct", "ctt", "nc"],
|
||
default="c",
|
||
type=str,
|
||
dest="fuller_reg",
|
||
)
|
||
parser.add_argument(
|
||
"-k",
|
||
"--kps_reg",
|
||
help="Type of regression. Can be 'c', 'ct'. c - Constant and t - trend order",
|
||
choices=["c", "ct"],
|
||
type=str,
|
||
dest="kpss_reg",
|
||
default="c",
|
||
)
|
||
|
||
if other_args and "-" not in other_args[0][0]:
|
||
other_args.insert(0, "-v")
|
||
ns_parser = self.parse_known_args_and_warn(
|
||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||
)
|
||
|
||
if ns_parser and ns_parser.column:
|
||
column, dataset = self.choices["root"][ns_parser.column].keys()
|
||
|
||
if isinstance(self.datasets[dataset][column].index, pd.MultiIndex):
|
||
console.print(
|
||
f"The column '{column}' from the dataset '{dataset}' is a MultiIndex. To test for unitroot in a "
|
||
"timeseries, make sure to set a singular time index.\n"
|
||
)
|
||
else:
|
||
if isinstance(self.datasets[dataset], pd.Series):
|
||
data = self.datasets[dataset]
|
||
elif isinstance(self.datasets[dataset], pd.DataFrame):
|
||
data = self.datasets[dataset][column]
|
||
else:
|
||
return console.print(
|
||
"Can not select data due to the data not being a DataFrame or Series."
|
||
)
|
||
|
||
econometrics_view.display_root(
|
||
data,
|
||
dataset,
|
||
column,
|
||
ns_parser.fuller_reg,
|
||
ns_parser.kpss_reg,
|
||
ns_parser.export,
|
||
)
|
||
|
||
@log_start_end(log=logger)
|
||
def call_panel(self, other_args: List[str]):
|
||
"""Process panel command"""
|
||
parser = argparse.ArgumentParser(
|
||
add_help=False,
|
||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||
prog="panel",
|
||
description="Performs regression analysis on Panel Data. There are a multitude of options to select "
|
||
"from to fit the needs of restrictions of the dataset.",
|
||
)
|
||
parser.add_argument(
|
||
"-d",
|
||
"--dependent",
|
||
type=str,
|
||
dest="dependent",
|
||
help="The dependent variable on the regression you would like to perform",
|
||
required="-h" not in other_args,
|
||
)
|
||
parser.add_argument(
|
||
"-i",
|
||
"--independent",
|
||
type=check_list_values(self.choices["regressions"]),
|
||
dest="independent",
|
||
help=(
|
||
"The independent variables on the regression you would like to perform. "
|
||
"E.g. wage_panel.married,wage_panel.union"
|
||
),
|
||
required="-h" not in other_args,
|
||
)
|
||
parser.add_argument(
|
||
"-r",
|
||
"--regression",
|
||
type=str,
|
||
choices=[
|
||
"pols",
|
||
"re",
|
||
"bols",
|
||
"fe",
|
||
"fdols",
|
||
"POLS",
|
||
"RE",
|
||
"BOLS",
|
||
"FE",
|
||
"FDOLS",
|
||
],
|
||
dest="type",
|
||
help="The type of regression you wish to perform. This can be either pols (Pooled OLS), "
|
||
"re (Random Effects), bols (Between OLS), fe (Fixed Effects) or fdols (First Difference OLS)",
|
||
default="pols",
|
||
)
|
||
parser.add_argument(
|
||
"-e",
|
||
"--entity_effects",
|
||
dest="entity_effects",
|
||
help="Using this command creates entity effects, which is equivalent to including dummies for each entity. "
|
||
"This is only used within Fixed Effects estimations (when type is set to 'fe')",
|
||
action="store_true",
|
||
default=False,
|
||
)
|
||
parser.add_argument(
|
||
"-t",
|
||
"--time_effects",
|
||
dest="time_effects",
|
||
help="Using this command creates time effects, which is equivalent to including dummies for each time. "
|
||
"This is only used within Fixed Effects estimations (when type is set to 'fe')",
|
||
action="store_true",
|
||
default=False,
|
||
)
|
||
if other_args and "-" not in other_args[0][0]:
|
||
other_args.insert(0, "-d")
|
||
ns_parser = self.parse_known_args_and_warn(
|
||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||
)
|
||
|
||
if ns_parser:
|
||
if "," in ns_parser.dependent:
|
||
console.print(
|
||
"It appears you have selected multiple variables for the dependent variable. "
|
||
"The model only accepts one.\nDid you intend to include these variables as independent "
|
||
f"variables? Use -i {ns_parser.dependent} in this case.\n"
|
||
)
|
||
elif ns_parser.dependent in self.choices["regressions"]:
|
||
regression_vars = [ns_parser.dependent] + ns_parser.independent
|
||
|
||
if regression_vars and len(regression_vars) > 1:
|
||
for variable in regression_vars:
|
||
column, dataset = self.choices["regressions"][variable].keys()
|
||
if not isinstance(
|
||
self.datasets[dataset][column].index, pd.MultiIndex
|
||
):
|
||
other_column = (
|
||
self.datasets[dataset].drop(column, axis=1).columns[0]
|
||
)
|
||
return console.print(
|
||
f"The column '{column}' from the dataset '{dataset}' is not a MultiIndex. Make sure "
|
||
f"you set the index correctly with the index (e.g. index {dataset} -i {column},"
|
||
f"{other_column}) command where the first level is the entity (e.g. Tesla Inc.) and "
|
||
f"the second level the date (e.g. 2021-03-31)\n"
|
||
)
|
||
|
||
# Ensure that OLS is always ran to be able to perform tests
|
||
regression_types = ["OLS", ns_parser.type.upper()]
|
||
|
||
for regression in regression_types:
|
||
regression_name = regression
|
||
if regression == "FE":
|
||
if ns_parser.entity_effects:
|
||
regression_name = regression_name + "_EE"
|
||
if ns_parser.time_effects:
|
||
regression_name = regression_name + "_IE"
|
||
|
||
(
|
||
self.regression[regression_name]["data"],
|
||
self.regression[regression_name]["dependent"],
|
||
self.regression[regression_name]["independent"],
|
||
self.regression[regression_name]["model"],
|
||
) = openbb_terminal.econometrics.regression_view.display_panel(
|
||
regression,
|
||
regression_vars,
|
||
self.datasets,
|
||
self.choices["regressions"],
|
||
ns_parser.entity_effects,
|
||
ns_parser.time_effects,
|
||
)
|
||
console.print()
|
||
else:
|
||
console.print(
|
||
f"{ns_parser.dependent} not in {','.join(self.choices['regressions'])}\n"
|
||
f"Please choose a valid dataset and column combination.\n"
|
||
)
|
||
|
||
@log_start_end(log=logger)
|
||
def call_compare(self, other_args: List[str]):
|
||
"""Process compare command"""
|
||
parser = argparse.ArgumentParser(
|
||
add_help=False,
|
||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||
prog="compare",
|
||
description="Compare results between all activated Panel regression models",
|
||
)
|
||
ns_parser = self.parse_known_args_and_warn(
|
||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||
)
|
||
if ns_parser:
|
||
openbb_terminal.econometrics.regression_model.get_comparison(
|
||
self.regression, ns_parser.export
|
||
)
|
||
console.print()
|
||
|
||
@log_start_end(log=logger)
|
||
def call_dwat(self, other_args: List[str]):
|
||
"""Process unitroot command"""
|
||
parser = argparse.ArgumentParser(
|
||
add_help=False,
|
||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||
prog="dwat",
|
||
description=(
|
||
"Show autocorrelation tests from Durbin-Watson. "
|
||
"Needs OLS to be run in advance with independent and dependent variables"
|
||
),
|
||
)
|
||
parser.add_argument(
|
||
"-p",
|
||
"--plot",
|
||
help="Plot the residuals",
|
||
dest="plot",
|
||
action="store_true",
|
||
default=False,
|
||
)
|
||
ns_parser = self.parse_known_args_and_warn(
|
||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||
)
|
||
if ns_parser:
|
||
if not self.regression["OLS"]["model"]:
|
||
console.print(
|
||
"Please perform an OLS regression before estimating the Durbin-Watson statistic.\n"
|
||
)
|
||
else:
|
||
dependent_variable = self.regression["OLS"]["data"][
|
||
self.regression["OLS"]["dependent"]
|
||
]
|
||
openbb_terminal.econometrics.regression_view.display_dwat(
|
||
dependent_variable,
|
||
self.regression["OLS"]["model"].resid,
|
||
ns_parser.plot,
|
||
ns_parser.export,
|
||
)
|
||
|
||
@log_start_end(log=logger)
|
||
def call_bgod(self, other_args):
|
||
"""Process bgod command"""
|
||
parser = argparse.ArgumentParser(
|
||
add_help=False,
|
||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||
prog="bgod",
|
||
description=(
|
||
"Show Breusch-Godfrey autocorrelation test results."
|
||
"Needs OLS to be run in advance with independent and dependent variables"
|
||
),
|
||
)
|
||
parser.add_argument(
|
||
"-l",
|
||
"--lags",
|
||
type=check_positive,
|
||
dest="lags",
|
||
help="The lags for the Breusch-Godfrey test",
|
||
default=3,
|
||
)
|
||
if other_args and "-" not in other_args[0][0]:
|
||
other_args.insert(0, "-l")
|
||
|
||
ns_parser = self.parse_known_args_and_warn(
|
||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||
)
|
||
|
||
if ns_parser:
|
||
if not self.regression["OLS"]["model"]:
|
||
console.print(
|
||
"Please perform an OLS regression before estimating the Breusch-Godfrey statistic.\n"
|
||
)
|
||
else:
|
||
openbb_terminal.econometrics.regression_view.display_bgod(
|
||
self.regression["OLS"]["model"], ns_parser.lags, ns_parser.export
|
||
)
|
||
|
||
@log_start_end(log=logger)
|
||
def call_bpag(self, other_args):
|
||
"""Process bpag command"""
|
||
parser = argparse.ArgumentParser(
|
||
add_help=False,
|
||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||
prog="bpag",
|
||
description=(
|
||
"Show Breusch-Pagan heteroscedasticity test results."
|
||
"Needs OLS to be run in advance with independent and dependent variables"
|
||
),
|
||
)
|
||
|
||
ns_parser = self.parse_known_args_and_warn(
|
||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||
)
|
||
|
||
if ns_parser:
|
||
if not self.regression["OLS"]["model"]:
|
||
console.print(
|
||
"Please perform an OLS regression before estimating the Breusch-Pagan statistic.\n"
|
||
)
|
||
else:
|
||
openbb_terminal.econometrics.regression_view.display_bpag(
|
||
self.regression["OLS"]["model"], ns_parser.export
|
||
)
|
||
|
||
@log_start_end(log=logger)
|
||
def call_granger(self, other_args: List[str]):
|
||
"""Process granger command"""
|
||
parser = argparse.ArgumentParser(
|
||
add_help=False,
|
||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||
prog="granger",
|
||
description="Show Granger causality between two timeseries",
|
||
)
|
||
parser.add_argument(
|
||
"-t",
|
||
"--timeseries",
|
||
choices=self.choices["granger"],
|
||
help="Requires two time series, the first time series is assumed to be Granger-caused "
|
||
"by the second time series.",
|
||
type=str,
|
||
dest="ts",
|
||
)
|
||
parser.add_argument(
|
||
"-l",
|
||
"--lags",
|
||
help="How many lags should be included",
|
||
type=int,
|
||
dest="lags",
|
||
default=3,
|
||
)
|
||
parser.add_argument(
|
||
"-c",
|
||
"--confidence",
|
||
help="Set the confidence level",
|
||
type=check_positive_float,
|
||
dest="confidence",
|
||
default=0.05,
|
||
)
|
||
|
||
if other_args and "-" not in other_args[0][0]:
|
||
other_args.insert(0, "-t")
|
||
ns_parser = self.parse_known_args_and_warn(
|
||
parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||
)
|
||
|
||
if ns_parser and ns_parser.ts:
|
||
|
||
datasetcol_y, datasetcol_x = ns_parser.ts.split(",")
|
||
|
||
dataset_y, column_y = datasetcol_y.split(".")
|
||
dataset_x, column_x = datasetcol_x.split(".")
|
||
|
||
econometrics_view.display_granger(
|
||
self.datasets[dataset_y][column_y].rename(datasetcol_y),
|
||
self.datasets[dataset_x][column_x].rename(datasetcol_x),
|
||
ns_parser.lags,
|
||
ns_parser.confidence,
|
||
ns_parser.export,
|
||
)
|
||
|
||
@log_start_end(log=logger)
|
||
def call_coint(self, other_args: List[str]):
|
||
"""Process coint command"""
|
||
parser = argparse.ArgumentParser(
|
||
add_help=False,
|
||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||
prog="coint",
|
||
description="Show co-integration between two timeseries",
|
||
)
|
||
parser.add_argument(
|
||
"-t",
|
||
"--time_series",
|
||
help="The time series you wish to test co-integration on. E.g. historical.open,historical2.close.",
|
||
dest="ts",
|
||
type=check_list_values(self.choices["coint"]),
|
||
required="-h" not in other_args,
|
||
)
|
||
parser.add_argument(
|
||
"-p",
|
||
"--plot",
|
||
help="Plot Z-Values",
|
||
dest="plot",
|
||
action="store_true",
|
||
default=False,
|
||
)
|
||
parser.add_argument(
|
||
"-s",
|
||
"--significant",
|
||
help="Show only companies that have p-values lower than this percentage",
|
||
dest="significant",
|
||
type=float,
|
||
default=0,
|
||
)
|
||
if other_args and "-" not in other_args[0][0]:
|
||
other_args.insert(0, "-t")
|
||
|
||
ns_parser = self.parse_known_args_and_warn(
|
||
parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||
)
|
||
|
||
if ns_parser:
|
||
|
||
if ns_parser.ts:
|
||
|
||
if len(ns_parser.ts) > 2:
|
||
datasets = {}
|
||
for stock in ns_parser.ts:
|
||
column, dataset = self.choices["coint"][stock].keys()
|
||
datasets[stock] = self.datasets[dataset][column]
|
||
|
||
econometrics_view.display_cointegration_test(
|
||
datasets,
|
||
ns_parser.significant,
|
||
ns_parser.plot,
|
||
ns_parser.export,
|
||
)
|
||
|
||
else:
|
||
console.print(
|
||
"[red]More than one dataset.column must be provided.\n[/red]"
|
||
)
|