| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,242 @@ | ||
| @ECHO OFF | ||
|
|
||
| REM Command file for Sphinx documentation | ||
|
|
||
| if "%SPHINXBUILD%" == "" ( | ||
| set SPHINXBUILD=sphinx-build | ||
| ) | ||
| set BUILDDIR=build | ||
| set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source | ||
| set I18NSPHINXOPTS=%SPHINXOPTS% source | ||
| if NOT "%PAPER%" == "" ( | ||
| set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% | ||
| set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% | ||
| ) | ||
|
|
||
| if "%1" == "" goto help | ||
|
|
||
| if "%1" == "help" ( | ||
| :help | ||
| echo.Please use `make ^<target^>` where ^<target^> is one of | ||
| echo. html to make standalone HTML files | ||
| echo. dirhtml to make HTML files named index.html in directories | ||
| echo. singlehtml to make a single large HTML file | ||
| echo. pickle to make pickle files | ||
| echo. json to make JSON files | ||
| echo. htmlhelp to make HTML files and a HTML help project | ||
| echo. qthelp to make HTML files and a qthelp project | ||
| echo. devhelp to make HTML files and a Devhelp project | ||
| echo. epub to make an epub | ||
| echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter | ||
| echo. text to make text files | ||
| echo. man to make manual pages | ||
| echo. texinfo to make Texinfo files | ||
| echo. gettext to make PO message catalogs | ||
| echo. changes to make an overview over all changed/added/deprecated items | ||
| echo. xml to make Docutils-native XML files | ||
| echo. pseudoxml to make pseudoxml-XML files for display purposes | ||
| echo. linkcheck to check all external links for integrity | ||
| echo. doctest to run all doctests embedded in the documentation if enabled | ||
| goto end | ||
| ) | ||
|
|
||
| if "%1" == "clean" ( | ||
| for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i | ||
| del /q /s %BUILDDIR%\* | ||
| goto end | ||
| ) | ||
|
|
||
|
|
||
| %SPHINXBUILD% 2> nul | ||
| if errorlevel 9009 ( | ||
| echo. | ||
| echo.The 'sphinx-build' command was not found. Make sure you have Sphinx | ||
| echo.installed, then set the SPHINXBUILD environment variable to point | ||
| echo.to the full path of the 'sphinx-build' executable. Alternatively you | ||
| echo.may add the Sphinx directory to PATH. | ||
| echo. | ||
| echo.If you don't have Sphinx installed, grab it from | ||
| echo.http://sphinx-doc.org/ | ||
| exit /b 1 | ||
| ) | ||
|
|
||
| if "%1" == "html" ( | ||
| %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html | ||
| if errorlevel 1 exit /b 1 | ||
| echo. | ||
| echo.Build finished. The HTML pages are in %BUILDDIR%/html. | ||
| goto end | ||
| ) | ||
|
|
||
| if "%1" == "dirhtml" ( | ||
| %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml | ||
| if errorlevel 1 exit /b 1 | ||
| echo. | ||
| echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. | ||
| goto end | ||
| ) | ||
|
|
||
| if "%1" == "singlehtml" ( | ||
| %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml | ||
| if errorlevel 1 exit /b 1 | ||
| echo. | ||
| echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. | ||
| goto end | ||
| ) | ||
|
|
||
| if "%1" == "pickle" ( | ||
| %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle | ||
| if errorlevel 1 exit /b 1 | ||
| echo. | ||
| echo.Build finished; now you can process the pickle files. | ||
| goto end | ||
| ) | ||
|
|
||
| if "%1" == "json" ( | ||
| %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json | ||
| if errorlevel 1 exit /b 1 | ||
| echo. | ||
| echo.Build finished; now you can process the JSON files. | ||
| goto end | ||
| ) | ||
|
|
||
| if "%1" == "htmlhelp" ( | ||
| %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp | ||
| if errorlevel 1 exit /b 1 | ||
| echo. | ||
| echo.Build finished; now you can run HTML Help Workshop with the ^ | ||
| .hhp project file in %BUILDDIR%/htmlhelp. | ||
| goto end | ||
| ) | ||
|
|
||
| if "%1" == "qthelp" ( | ||
| %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp | ||
| if errorlevel 1 exit /b 1 | ||
| echo. | ||
| echo.Build finished; now you can run "qcollectiongenerator" with the ^ | ||
| .qhcp project file in %BUILDDIR%/qthelp, like this: | ||
| echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Ibis.qhcp | ||
| echo.To view the help file: | ||
| echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Ibis.ghc | ||
| goto end | ||
| ) | ||
|
|
||
| if "%1" == "devhelp" ( | ||
| %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp | ||
| if errorlevel 1 exit /b 1 | ||
| echo. | ||
| echo.Build finished. | ||
| goto end | ||
| ) | ||
|
|
||
| if "%1" == "epub" ( | ||
| %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub | ||
| if errorlevel 1 exit /b 1 | ||
| echo. | ||
| echo.Build finished. The epub file is in %BUILDDIR%/epub. | ||
| goto end | ||
| ) | ||
|
|
||
| if "%1" == "latex" ( | ||
| %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex | ||
| if errorlevel 1 exit /b 1 | ||
| echo. | ||
| echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. | ||
| goto end | ||
| ) | ||
|
|
||
| if "%1" == "latexpdf" ( | ||
| %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex | ||
| cd %BUILDDIR%/latex | ||
| make all-pdf | ||
| cd %BUILDDIR%/.. | ||
| echo. | ||
| echo.Build finished; the PDF files are in %BUILDDIR%/latex. | ||
| goto end | ||
| ) | ||
|
|
||
| if "%1" == "latexpdfja" ( | ||
| %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex | ||
| cd %BUILDDIR%/latex | ||
| make all-pdf-ja | ||
| cd %BUILDDIR%/.. | ||
| echo. | ||
| echo.Build finished; the PDF files are in %BUILDDIR%/latex. | ||
| goto end | ||
| ) | ||
|
|
||
| if "%1" == "text" ( | ||
| %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text | ||
| if errorlevel 1 exit /b 1 | ||
| echo. | ||
| echo.Build finished. The text files are in %BUILDDIR%/text. | ||
| goto end | ||
| ) | ||
|
|
||
| if "%1" == "man" ( | ||
| %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man | ||
| if errorlevel 1 exit /b 1 | ||
| echo. | ||
| echo.Build finished. The manual pages are in %BUILDDIR%/man. | ||
| goto end | ||
| ) | ||
|
|
||
| if "%1" == "texinfo" ( | ||
| %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo | ||
| if errorlevel 1 exit /b 1 | ||
| echo. | ||
| echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. | ||
| goto end | ||
| ) | ||
|
|
||
| if "%1" == "gettext" ( | ||
| %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale | ||
| if errorlevel 1 exit /b 1 | ||
| echo. | ||
| echo.Build finished. The message catalogs are in %BUILDDIR%/locale. | ||
| goto end | ||
| ) | ||
|
|
||
| if "%1" == "changes" ( | ||
| %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes | ||
| if errorlevel 1 exit /b 1 | ||
| echo. | ||
| echo.The overview file is in %BUILDDIR%/changes. | ||
| goto end | ||
| ) | ||
|
|
||
| if "%1" == "linkcheck" ( | ||
| %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck | ||
| if errorlevel 1 exit /b 1 | ||
| echo. | ||
| echo.Link check complete; look for any errors in the above output ^ | ||
| or in %BUILDDIR%/linkcheck/output.txt. | ||
| goto end | ||
| ) | ||
|
|
||
| if "%1" == "doctest" ( | ||
| %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest | ||
| if errorlevel 1 exit /b 1 | ||
| echo. | ||
| echo.Testing of doctests in the sources finished, look at the ^ | ||
| results in %BUILDDIR%/doctest/output.txt. | ||
| goto end | ||
| ) | ||
|
|
||
| if "%1" == "xml" ( | ||
| %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml | ||
| if errorlevel 1 exit /b 1 | ||
| echo. | ||
| echo.Build finished. The XML files are in %BUILDDIR%/xml. | ||
| goto end | ||
| ) | ||
|
|
||
| if "%1" == "pseudoxml" ( | ||
| %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml | ||
| if errorlevel 1 exit /b 1 | ||
| echo. | ||
| echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. | ||
| goto end | ||
| ) | ||
|
|
||
| :end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| sphinx_rtd_theme | ||
| numpydoc |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| === | ||
| API | ||
| === |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,269 @@ | ||
| # -*- coding: utf-8 -*- | ||
| # | ||
| # Ibis documentation build configuration file, created by | ||
| # sphinx-quickstart on Wed Jun 10 11:06:29 2015. | ||
| # | ||
| # This file is execfile()d with the current directory set to its | ||
| # containing dir. | ||
| # | ||
| # Note that not all possible configuration values are present in this | ||
| # autogenerated file. | ||
| # | ||
| # All configuration values have a default; values that are commented out | ||
| # serve to show the default. | ||
|
|
||
| import sys | ||
| import os | ||
|
|
||
| # If extensions (or modules to document with autodoc) are in another directory, | ||
| # add these directories to sys.path here. If the directory is relative to the | ||
| # documentation root, use os.path.abspath to make it absolute, like shown here. | ||
| #sys.path.insert(0, os.path.abspath('.')) | ||
|
|
||
| # -- General configuration ------------------------------------------------ | ||
|
|
||
| # If your documentation needs a minimal Sphinx version, state it here. | ||
| #needs_sphinx = '1.0' | ||
|
|
||
| # Add any Sphinx extension module names here, as strings. They can be | ||
| # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom | ||
| # ones. | ||
| extensions = [ | ||
| 'sphinx.ext.autodoc', | ||
| 'sphinx.ext.mathjax', | ||
| 'sphinx.ext.autosummary', | ||
| 'numpydoc' | ||
| ] | ||
|
|
||
| # Add any paths that contain templates here, relative to this directory. | ||
| templates_path = ['_templates'] | ||
|
|
||
| # The suffix of source filenames. | ||
| source_suffix = '.rst' | ||
|
|
||
| # The encoding of source files. | ||
| #source_encoding = 'utf-8-sig' | ||
|
|
||
| # The master toctree document. | ||
| master_doc = 'index' | ||
|
|
||
| # General information about the project. | ||
| project = u'Ibis' | ||
| copyright = u'2015, Cloudera, Inc.' | ||
|
|
||
| # The version info for the project you're documenting, acts as replacement for | ||
| # |version| and |release|, also used in various other places throughout the | ||
| # built documents. | ||
| # | ||
| # The short X.Y version. | ||
| # version = '0.2' | ||
|
|
||
| from ibis import __version__ as version | ||
|
|
||
| # The full version, including alpha/beta/rc tags. | ||
| release = version | ||
|
|
||
| # The language for content autogenerated by Sphinx. Refer to documentation | ||
| # for a list of supported languages. | ||
| #language = None | ||
|
|
||
| # There are two options for replacing |today|: either, you set today to some | ||
| # non-false value, then it is used: | ||
| #today = '' | ||
| # Else, today_fmt is used as the format for a strftime call. | ||
| #today_fmt = '%B %d, %Y' | ||
|
|
||
| # List of patterns, relative to source directory, that match files and | ||
| # directories to ignore when looking for source files. | ||
| exclude_patterns = [] | ||
|
|
||
| # The reST default role (used for this markup: `text`) to use for all | ||
| # documents. | ||
| #default_role = None | ||
|
|
||
| # If true, '()' will be appended to :func: etc. cross-reference text. | ||
| #add_function_parentheses = True | ||
|
|
||
| # If true, the current module name will be prepended to all description | ||
| # unit titles (such as .. function::). | ||
| #add_module_names = True | ||
|
|
||
| # If true, sectionauthor and moduleauthor directives will be shown in the | ||
| # output. They are ignored by default. | ||
| #show_authors = False | ||
|
|
||
| # The name of the Pygments (syntax highlighting) style to use. | ||
| pygments_style = 'sphinx' | ||
|
|
||
| # A list of ignored prefixes for module index sorting. | ||
| #modindex_common_prefix = [] | ||
|
|
||
| # If true, keep warnings as "system message" paragraphs in the built documents. | ||
| #keep_warnings = False | ||
|
|
||
|
|
||
| # -- Options for HTML output ---------------------------------------------- | ||
|
|
||
| # The theme to use for HTML and HTML Help pages. See the documentation for | ||
| # a list of builtin themes. | ||
|
|
||
| import sphinx_rtd_theme | ||
| html_theme = "sphinx_rtd_theme" | ||
| html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] | ||
|
|
||
| # Theme options are theme-specific and customize the look and feel of a theme | ||
| # further. For a list of options available for each theme, see the | ||
| # documentation. | ||
| #html_theme_options = {} | ||
|
|
||
| # Add any paths that contain custom themes here, relative to this directory. | ||
| #html_theme_path = [] | ||
|
|
||
| # The name for this set of Sphinx documents. If None, it defaults to | ||
| # "<project> v<release> documentation". | ||
| #html_title = None | ||
|
|
||
| # A shorter title for the navigation bar. Default is the same as html_title. | ||
| #html_short_title = None | ||
|
|
||
| # The name of an image file (relative to this directory) to place at the top | ||
| # of the sidebar. | ||
| #html_logo = None | ||
|
|
||
| # The name of an image file (within the static path) to use as favicon of the | ||
| # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 | ||
| # pixels large. | ||
| #html_favicon = None | ||
|
|
||
| # Add any paths that contain custom static files (such as style sheets) here, | ||
| # relative to this directory. They are copied after the builtin static files, | ||
| # so a file named "default.css" will overwrite the builtin "default.css". | ||
| html_static_path = ['_static'] | ||
|
|
||
| # Add any extra paths that contain custom files (such as robots.txt or | ||
| # .htaccess) here, relative to this directory. These files are copied | ||
| # directly to the root of the documentation. | ||
| #html_extra_path = [] | ||
|
|
||
| # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, | ||
| # using the given strftime format. | ||
| #html_last_updated_fmt = '%b %d, %Y' | ||
|
|
||
| # If true, SmartyPants will be used to convert quotes and dashes to | ||
| # typographically correct entities. | ||
| #html_use_smartypants = True | ||
|
|
||
| # Custom sidebar templates, maps document names to template names. | ||
| #html_sidebars = {} | ||
|
|
||
| # Additional templates that should be rendered to pages, maps page names to | ||
| # template names. | ||
| #html_additional_pages = {} | ||
|
|
||
| # If false, no module index is generated. | ||
| #html_domain_indices = True | ||
|
|
||
| # If false, no index is generated. | ||
| #html_use_index = True | ||
|
|
||
| # If true, the index is split into individual pages for each letter. | ||
| #html_split_index = False | ||
|
|
||
| # If true, links to the reST sources are added to the pages. | ||
| #html_show_sourcelink = True | ||
|
|
||
| # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. | ||
| #html_show_sphinx = True | ||
|
|
||
| # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. | ||
| #html_show_copyright = True | ||
|
|
||
| # If true, an OpenSearch description file will be output, and all pages will | ||
| # contain a <link> tag referring to it. The value of this option must be the | ||
| # base URL from which the finished HTML is served. | ||
| #html_use_opensearch = '' | ||
|
|
||
| # This is the file name suffix for HTML files (e.g. ".xhtml"). | ||
| #html_file_suffix = None | ||
|
|
||
| # Output file base name for HTML help builder. | ||
| htmlhelp_basename = 'Ibisdoc' | ||
|
|
||
|
|
||
| # -- Options for LaTeX output --------------------------------------------- | ||
|
|
||
| latex_elements = { | ||
| # The paper size ('letterpaper' or 'a4paper'). | ||
| #'papersize': 'letterpaper', | ||
|
|
||
| # The font size ('10pt', '11pt' or '12pt'). | ||
| #'pointsize': '10pt', | ||
|
|
||
| # Additional stuff for the LaTeX preamble. | ||
| #'preamble': '', | ||
| } | ||
|
|
||
| # Grouping the document tree into LaTeX files. List of tuples | ||
| # (source start file, target name, title, | ||
| # author, documentclass [howto, manual, or own class]). | ||
| latex_documents = [ | ||
| ('index', 'Ibis.tex', u'Ibis Documentation', | ||
| u'Cloudera, Inc.', 'manual'), | ||
| ] | ||
|
|
||
| # The name of an image file (relative to this directory) to place at the top of | ||
| # the title page. | ||
| #latex_logo = None | ||
|
|
||
| # For "manual" documents, if this is true, then toplevel headings are parts, | ||
| # not chapters. | ||
| #latex_use_parts = False | ||
|
|
||
| # If true, show page references after internal links. | ||
| #latex_show_pagerefs = False | ||
|
|
||
| # If true, show URL addresses after external links. | ||
| #latex_show_urls = False | ||
|
|
||
| # Documents to append as an appendix to all manuals. | ||
| #latex_appendices = [] | ||
|
|
||
| # If false, no module index is generated. | ||
| #latex_domain_indices = True | ||
|
|
||
|
|
||
| # -- Options for manual page output --------------------------------------- | ||
|
|
||
| # One entry per manual page. List of tuples | ||
| # (source start file, name, description, authors, manual section). | ||
| man_pages = [ | ||
| ('index', 'ibis', u'Ibis Documentation', | ||
| [u'Cloudera, Inc.'], 1) | ||
| ] | ||
|
|
||
| # If true, show URL addresses after external links. | ||
| #man_show_urls = False | ||
|
|
||
|
|
||
| # -- Options for Texinfo output ------------------------------------------- | ||
|
|
||
| # Grouping the document tree into Texinfo files. List of tuples | ||
| # (source start file, target name, title, author, | ||
| # dir menu entry, description, category) | ||
| texinfo_documents = [ | ||
| ('index', 'Ibis', u'Ibis Documentation', | ||
| u'Cloudera, Inc.', 'Ibis', 'One line description of project.', | ||
| 'Miscellaneous'), | ||
| ] | ||
|
|
||
| # Documents to append as an appendix to all manuals. | ||
| #texinfo_appendices = [] | ||
|
|
||
| # If false, no module index is generated. | ||
| #texinfo_domain_indices = True | ||
|
|
||
| # How to display URL addresses: 'footnote', 'no', or 'inline'. | ||
| #texinfo_show_urls = 'footnote' | ||
|
|
||
| # If true, do not generate a @detailmenu in the "Top" node's menu. | ||
| #texinfo_no_detailmenu = False |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| .. Ibis documentation master file, created by | ||
| sphinx-quickstart on Wed Jun 10 11:06:29 2015. | ||
| You can adapt this file completely to your liking, but it should at least | ||
| contain the root `toctree` directive. | ||
| Ibis | ||
| ==== | ||
|
|
||
| Contents: | ||
|
|
||
| .. toctree:: | ||
| :maxdepth: 1 | ||
|
|
||
| api | ||
| release | ||
| legal | ||
|
|
||
| Indices and tables | ||
| ================== | ||
|
|
||
| * :ref:`genindex` | ||
| * :ref:`modindex` | ||
| * :ref:`search` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| ===== | ||
| Legal | ||
| ===== |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| ============= | ||
| Release Notes | ||
| ============= | ||
|
|
||
| 0.3.0 (TBD) | ||
| ----------- | ||
|
|
||
| 0.2.0 (June 16, 2015) | ||
| --------------------- | ||
|
|
||
| New features | ||
| ~~~~~~~~~~~~ | ||
| * ``insert`` method on Ibis client for inserting data into existing tables. | ||
| * ``parquet_file``, ``delimited_file``, and ``avro_file`` client methods for | ||
| querying datasets not yet available in Impala | ||
| * New ``ibis.hdfs_connect`` method and ``HDFS`` client API for WebHDFS for | ||
| writing files and directories to HDFS | ||
| * New timedelta API and improved timestamp data support | ||
| * New ``bucket`` and ``histogram`` methods on numeric expressions | ||
| * New ``category`` logical datatype for handling bucketed data, among other | ||
| things | ||
| * Add ``summary`` API to numeric expressions | ||
| * Add ``value_counts`` convenience API to array expressions | ||
| * New string methods ``like``, ``rlike``, and ``contains`` for fuzzy and regex | ||
| searching | ||
| * Add ``options.verbose`` option and configurable ``options.verbose_log`` | ||
| callback function for improved query logging and visibility | ||
| * Support for new SQL built-in functions | ||
|
|
||
| * ``ibis.coalesce`` | ||
| * ``ibis.greatest`` and ``ibis.least`` | ||
| * ``ibis.where`` for conditional logic (see also ``ibis.case`` and | ||
| ``ibis.cases``) | ||
| * ``nullif`` method on value expressions | ||
| * ``ibis.now`` | ||
|
|
||
| * New aggregate functions: ``approx_median``, ``approx_nunique``, and | ||
| ``group_concat`` | ||
| * ``where`` argument in aggregate functions | ||
| * Add ``having`` method to ``group_by`` intermediate object | ||
| * Added group-by convenience | ||
| ``table.group_by(exprs).COLUMN_NAME.agg_function()`` | ||
| * Add default expression names to most aggregate functions | ||
| * New Impala database client helper methods | ||
|
|
||
| * ``create_database`` | ||
| * ``drop_database`` | ||
| * ``exists_database`` | ||
| * ``list_databases`` | ||
| * ``set_database`` | ||
|
|
||
| * Client ``list_tables`` searching / listing method | ||
| * Add ``add``, ``sub``, and other explicit arithmetic methods to value | ||
| expressions | ||
|
|
||
| API Changes | ||
| ~~~~~~~~~~~ | ||
| * New Ibis client and Impala connection workflow. Client now combined from an | ||
| Impala connection and an optional HDFS connection | ||
|
|
||
| Bug fixes | ||
| ~~~~~~~~~ | ||
| * Numerous expression API bug fixes and rough edges fixed | ||
|
|
||
| 0.1.0 (March 26, 2015) | ||
| ---------------------- |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,178 @@ | ||
| # Copyright 2015 Cloudera Inc. | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
|
|
||
| import ibis.expr.types as ir | ||
| import ibis.expr.operations as ops | ||
|
|
||
|
|
||
| class BucketLike(ir.ValueNode): | ||
|
|
||
| def _validate_closed(self, closed): | ||
| closed = closed.lower() | ||
| if closed not in ['left', 'right']: | ||
| raise ValueError("closed must be 'left' or 'right'") | ||
| return closed | ||
|
|
||
| @property | ||
| def nbuckets(self): | ||
| return None | ||
|
|
||
| def output_type(self): | ||
| ctype = ir.CategoryType(self.nbuckets) | ||
| return ctype.array_ctor() | ||
|
|
||
|
|
||
| class Bucket(BucketLike): | ||
|
|
||
| def __init__(self, arg, buckets, closed='left', close_extreme=True, | ||
| include_under=False, include_over=False): | ||
| self.arg = arg | ||
| self.buckets = buckets | ||
| self.closed = self._validate_closed(closed) | ||
|
|
||
| self.close_extreme = bool(close_extreme) | ||
| self.include_over = bool(include_over) | ||
| self.include_under = bool(include_under) | ||
|
|
||
| if len(buckets) == 0: | ||
| raise ValueError('Must be at least one bucket edge') | ||
| elif len(buckets) == 1: | ||
| if not self.include_under or not self.include_over: | ||
| raise ValueError('If one bucket edge provided, must have' | ||
| ' include_under=True and include_over=True') | ||
|
|
||
| ir.ValueNode.__init__(self, [self.arg, self.buckets, self.closed, | ||
| self.close_extreme, | ||
| self.include_under, | ||
| self.include_over]) | ||
|
|
||
| @property | ||
| def nbuckets(self): | ||
| k = len(self.buckets) - 1 | ||
| k += int(self.include_over) + int(self.include_under) | ||
| return k | ||
|
|
||
|
|
||
| class Histogram(BucketLike): | ||
|
|
||
| def __init__(self, arg, nbins, binwidth, base, closed='left', | ||
| aux_hash=None): | ||
| self.arg = arg | ||
|
|
||
| self.nbins = nbins | ||
| self.binwidth = binwidth | ||
| self.base = base | ||
|
|
||
| if self.nbins is None: | ||
| if self.binwidth is None: | ||
| raise ValueError('Must indicate nbins or binwidth') | ||
| elif self.binwidth is not None: | ||
| raise ValueError('nbins and binwidth are mutually exclusive') | ||
|
|
||
| self.closed = self._validate_closed(closed) | ||
|
|
||
| self.aux_hash = aux_hash | ||
| ir.ValueNode.__init__(self, [self.arg, self.nbins, self.binwidth, | ||
| self.base, self.closed, self.aux_hash]) | ||
|
|
||
| def output_type(self): | ||
| # always undefined cardinality (for now) | ||
| ctype = ir.CategoryType() | ||
| return ctype.array_ctor() | ||
|
|
||
|
|
||
| class CategoryLabel(ir.ValueNode): | ||
|
|
||
| def __init__(self, arg, labels, nulls): | ||
| self.arg = ops.as_value_expr(arg) | ||
| self.labels = labels | ||
|
|
||
| card = self.arg.type().cardinality | ||
| if len(self.labels) != card: | ||
| raise ValueError('Number of labels must match number of ' | ||
| 'categories: %d' % card) | ||
|
|
||
| self.nulls = nulls | ||
| ir.ValueNode.__init__(self, [self.arg, self.labels, self.nulls]) | ||
|
|
||
| def output_type(self): | ||
| return ops._shape_like(self.arg, 'string') | ||
|
|
||
|
|
||
| def bucket(arg, buckets, closed='left', close_extreme=True, | ||
| include_under=False, include_over=False): | ||
| """ | ||
| Parameters | ||
| ---------- | ||
| arg : numeric array expression | ||
| buckets : list | ||
| closed : {'left', 'right'}, default 'left' | ||
| Which side of each interval is closed. For example | ||
| buckets = [0, 100, 200] | ||
| closed = 'left': 100 falls in 2nd bucket | ||
| closed = 'right': 100 falls in 1st bucket | ||
| close_extreme : boolean, default True | ||
| Returns | ||
| ------- | ||
| bucketed : coded value expression | ||
| """ | ||
| op = Bucket(arg, buckets, closed=closed, close_extreme=close_extreme, | ||
| include_under=include_under, include_over=include_over) | ||
| return op.to_expr() | ||
|
|
||
|
|
||
| def histogram(arg, nbins=None, binwidth=None, base=None, closed='left', | ||
| aux_hash=None): | ||
| """ | ||
| Compute a histogram with fixed width bins | ||
| Parameters | ||
| ---------- | ||
| arg : numeric array expression | ||
| nbins : int, default None | ||
| If supplied, will be used to compute the binwidth | ||
| binwidth : number, default None | ||
| If not supplied, computed from the data (actual max and min values) | ||
| base : number, default None | ||
| closed : {'left', 'right'}, default 'left' | ||
| Which side of each interval is closed | ||
| Returns | ||
| ------- | ||
| histogrammed : coded value expression | ||
| """ | ||
| op = Histogram(arg, nbins, binwidth, base, closed=closed, | ||
| aux_hash=aux_hash) | ||
| return op.to_expr() | ||
|
|
||
|
|
||
| def category_label(arg, labels, nulls=None): | ||
| """ | ||
| Format a known number of categories as strings | ||
| Parameters | ||
| ---------- | ||
| labels : list of string | ||
| nulls : string, optional | ||
| How to label any null values among the categories | ||
| Returns | ||
| ------- | ||
| string_categories : string value expression | ||
| """ | ||
| op = CategoryLabel(arg, labels, nulls) | ||
| return op.to_expr() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| # Copyright 2014 Cloudera Inc. | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| # User API for grouped data operations | ||
|
|
||
| import ibis.expr.types as ir | ||
| import ibis.util as util | ||
|
|
||
|
|
||
| class GroupedTableExpr(object): | ||
|
|
||
| """ | ||
| Helper intermediate construct | ||
| """ | ||
|
|
||
| def __init__(self, table, by, having=None): | ||
| if not isinstance(by, (list, tuple)): | ||
| if not isinstance(by, ir.Expr): | ||
| by = table._resolve([by]) | ||
| else: | ||
| by = [by] | ||
| else: | ||
| by = table._resolve(by) | ||
|
|
||
| self.table = table | ||
| self.by = by | ||
| self._having = having or [] | ||
|
|
||
| def __getattr__(self, attr): | ||
| if hasattr(self.table, attr): | ||
| return self._column_wrapper(attr) | ||
|
|
||
| raise AttributeError("GroupBy has no attribute %r" % attr) | ||
|
|
||
| def _column_wrapper(self, attr): | ||
| col = self.table[attr] | ||
| if isinstance(col, ir.NumericValue): | ||
| return GroupedNumbers(col, self) | ||
| else: | ||
| return GroupedArray(col, self) | ||
|
|
||
| def aggregate(self, metrics): | ||
| return self.table.aggregate(metrics, by=self.by, | ||
| having=self._having) | ||
|
|
||
| def having(self, expr): | ||
| """ | ||
| Add a post-aggregation result filter (like the having argument in | ||
| `aggregate`), for composability with the group_by API | ||
| Returns | ||
| ------- | ||
| grouped : GroupedTableExpr | ||
| """ | ||
| exprs = util.promote_list(expr) | ||
| new_having = self._having + exprs | ||
| return GroupedTableExpr(self.table, self.by, having=new_having) | ||
|
|
||
| def count(self, metric_name='count'): | ||
| """ | ||
| Convenience function for computing the group sizes (number of rows per | ||
| group) given a grouped table. | ||
| Parameters | ||
| ---------- | ||
| metric_name : string, default 'count' | ||
| Name to use for the row count metric | ||
| Returns | ||
| ------- | ||
| aggregated : TableExpr | ||
| The aggregated table | ||
| """ | ||
| metric = self.table.count().name(metric_name) | ||
| return self.table.aggregate([metric], by=self.by) | ||
|
|
||
| size = count | ||
|
|
||
|
|
||
| def _group_agg_dispatch(name): | ||
| def wrapper(self, *args, **kwargs): | ||
| f = getattr(self.arr, name) | ||
| metric = f(*args, **kwargs) | ||
| alias = '{}({})'.format(name, self.arr.get_name()) | ||
| return self.parent.aggregate(metric.name(alias)) | ||
|
|
||
| wrapper.__name__ = name | ||
| return wrapper | ||
|
|
||
|
|
||
| class GroupedArray(object): | ||
|
|
||
| def __init__(self, arr, parent): | ||
| self.arr = arr | ||
| self.parent = parent | ||
|
|
||
| count = _group_agg_dispatch('count') | ||
| size = count | ||
| min = _group_agg_dispatch('min') | ||
| max = _group_agg_dispatch('max') | ||
| approx_nunique = _group_agg_dispatch('approx_nunique') | ||
| approx_median = _group_agg_dispatch('approx_median') | ||
| group_concat = _group_agg_dispatch('group_concat') | ||
|
|
||
| def summary(self, exact_nunique=False): | ||
| metric = self.arr.summary(exact_nunique=exact_nunique) | ||
| return self.parent.aggregate(metric) | ||
|
|
||
|
|
||
| class GroupedNumbers(GroupedArray): | ||
|
|
||
| mean = _group_agg_dispatch('mean') | ||
| sum = _group_agg_dispatch('sum') | ||
|
|
||
| def summary(self, exact_nunique=False): | ||
| metric = self.arr.summary(exact_nunique=exact_nunique) | ||
| return self.parent.aggregate(metric) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,298 @@ | ||
| # Copyright 2014 Cloudera Inc. | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| from ibis.common import IbisError | ||
| import ibis.expr.types as ir | ||
|
|
||
|
|
||
| __all__ = ['timedelta', 'year', 'month', 'week', 'day', | ||
| 'hour', 'minute', 'second', | ||
| 'millisecond', 'microsecond'] | ||
|
|
||
|
|
||
| class Timedelta(object): | ||
| """ | ||
| Represents any kind of date/time/timestamp increment, the precise length | ||
| possibly dependent on the timestamp being modified. | ||
| """ | ||
| def __init__(self, n): | ||
| self.n = int(n) | ||
|
|
||
| @property | ||
| def unit(self): | ||
| raise NotImplementedError | ||
|
|
||
| @property | ||
| def unit_name(self): | ||
| return type(self).__name__.lower() | ||
|
|
||
| def __repr__(self): | ||
| if self.n == 1: | ||
| pretty_unit = self.unit_name | ||
| else: | ||
| pretty_unit = '{}s'.format(self.unit_name) | ||
|
|
||
| return '<Timedelta: {} {}>'.format(self.n, pretty_unit) | ||
|
|
||
| def replace(self, n): | ||
| return type(self)(n) | ||
|
|
||
| def __mul__(self, times): | ||
| return self.replace(self.n * times) | ||
|
|
||
| __rmul__ = __mul__ | ||
|
|
||
| def __add__(self, arg): | ||
| from ibis.expr.operations import TimestampDelta | ||
|
|
||
| if isinstance(arg, ir.TimestampValue): | ||
| op = TimestampDelta(arg, self) | ||
| return op.to_expr() | ||
| elif isinstance(arg, Timedelta): | ||
| return self.combine(arg) | ||
| else: | ||
| raise TypeError(arg) | ||
|
|
||
| __radd__ = __add__ | ||
|
|
||
| def __sub__(self, arg): | ||
| if isinstance(arg, ir.Expr): | ||
| raise TypeError(arg) | ||
| elif isinstance(arg, Timedelta): | ||
| return self.combine(arg.replace(-arg.n)) | ||
| else: | ||
| raise NotImplementedError | ||
|
|
||
| def __rsub__(self, arg): | ||
| return self.replace(-self.n).__add__(arg) | ||
|
|
||
| def combine(self, other): | ||
| if type(self) != type(other): | ||
| raise TypeError(type(other)) | ||
|
|
||
| klass = type(self) | ||
| return klass(self.n + other.n) | ||
|
|
||
| def equals(self, other): | ||
| if type(self) != type(other): | ||
| return False | ||
|
|
||
| return self.n == other.n | ||
|
|
||
|
|
||
| class TimeIncrement(Timedelta): | ||
|
|
||
| @property | ||
| def unit(self): | ||
| return self._unit | ||
|
|
||
| def combine(self, other): | ||
| if not isinstance(other, TimeIncrement): | ||
| raise TypeError('Must be a fixed size timedelta, was {!r}' | ||
| .format(type(other))) | ||
|
|
||
| a, b = _to_common_units([self, other]) | ||
| return type(a)(a.n + b.n) | ||
|
|
||
| def to_unit(self, target_unit): | ||
| """ | ||
| """ | ||
| target_unit = target_unit.lower() | ||
| if self.unit == target_unit: | ||
| return self | ||
|
|
||
| klass = _timedelta_units[target_unit] | ||
| increments = CONVERTER.convert(self.n, self.unit, target_unit) | ||
| return klass(increments) | ||
|
|
||
|
|
||
| def _to_common_units(args): | ||
| common_unit = CONVERTER.get_common_unit([x.unit for x in args]) | ||
| return [x.to_unit(common_unit) for x in args] | ||
|
|
||
|
|
||
| class Nanosecond(TimeIncrement): | ||
| _unit = 'ns' | ||
|
|
||
|
|
||
| class Microsecond(TimeIncrement): | ||
| _unit = 'us' | ||
|
|
||
|
|
||
| class Millisecond(TimeIncrement): | ||
| _unit = 'ms' | ||
|
|
||
|
|
||
| class Second(TimeIncrement): | ||
| _unit = 's' | ||
|
|
||
|
|
||
| class Minute(TimeIncrement): | ||
| _unit = 'm' | ||
|
|
||
|
|
||
| class Hour(TimeIncrement): | ||
| _unit = 'h' | ||
|
|
||
|
|
||
| class Day(TimeIncrement): | ||
| _unit = 'd' | ||
|
|
||
|
|
||
| class Week(TimeIncrement): | ||
| _unit = 'w' | ||
|
|
||
|
|
||
| class Month(Timedelta): | ||
| _unit = 'M' | ||
|
|
||
|
|
||
| class Year(Timedelta): | ||
| _unit = 'Y' | ||
|
|
||
|
|
||
| _timedelta_units = { | ||
| 'Y': Year, | ||
| 'M': Month, | ||
| 'w': Week, | ||
| 'd': Day, | ||
| 'h': Hour, | ||
| 'm': Minute, | ||
| 's': Second, | ||
| 'ms': Millisecond, | ||
| 'us': Microsecond, | ||
| 'ns': Nanosecond | ||
| } | ||
|
|
||
|
|
||
| class UnitConverter(object): | ||
|
|
||
| def __init__(self, ordering, conv_factors, names): | ||
| self.ordering = ordering | ||
| self.conv_factors = conv_factors | ||
| self.names = names | ||
|
|
||
| self.ranks = dict((name, i) for i, name in enumerate(ordering)) | ||
| self.rank_to_unit = dict((v, k) for k, v in self.ranks.items()) | ||
|
|
||
| def get_common_unit(self, units): | ||
| min_rank = max(self.ranks[x] for x in units) | ||
| return self.rank_to_unit[min_rank] | ||
|
|
||
| def convert(self, n, from_unit, to_unit): | ||
| i = self.ranks[from_unit] | ||
| j = self.ranks[to_unit] | ||
|
|
||
| if i == j: | ||
| return n | ||
|
|
||
| factors = self.conv_factors[min(i, j) + 1: max(i, j) + 1] | ||
| factor = 1 | ||
| for x in factors: | ||
| factor *= x | ||
|
|
||
| if j < i: | ||
| if n % factor: | ||
| raise IbisError('{} is not a multiple of {}'.format(n, factor)) | ||
| return n / factor | ||
| else: | ||
| return n * factor | ||
|
|
||
| def anglicize(self, n, unit): | ||
| raise NotImplementedError | ||
|
|
||
|
|
||
| _ordering = ['w', 'd', 'h', 'm', 's', 'ms', 'us', 'ns'] | ||
| _factors = [1, 7, 24, 60, 60, 1000, 1000, 1000] | ||
| _names = ['week', 'day', 'hour', 'minute', 'second', | ||
| 'millisecond', 'microsecond', 'nanosecond'] | ||
|
|
||
|
|
||
| CONVERTER = UnitConverter(_ordering, _factors, _names) | ||
|
|
||
|
|
||
| def _delta_factory(name, unit): | ||
| klass = _timedelta_units[unit] | ||
|
|
||
| def factory(n=1): | ||
| return klass(n) | ||
|
|
||
| factory.__name__ = name | ||
|
|
||
| return factory | ||
|
|
||
| nanosecond = _delta_factory('nanosecond', 'ns') | ||
| microsecond = _delta_factory('microsecond', 'us') | ||
| millisecond = _delta_factory('millisecond', 'ms') | ||
| second = _delta_factory('second', 's') | ||
| minute = _delta_factory('minute', 'm') | ||
| hour = _delta_factory('hour', 'h') | ||
| day = _delta_factory('day', 'd') | ||
| week = _delta_factory('week', 'w') | ||
| month = _delta_factory('month', 'M') | ||
| year = _delta_factory('year', 'Y') | ||
|
|
||
|
|
||
| def timedelta(days=None, hours=None, minutes=None, seconds=None, | ||
| milliseconds=None, microseconds=None, nanoseconds=None, | ||
| weeks=None): | ||
| """ | ||
| Generic API for creating a fixed size timedelta | ||
| Parameters | ||
| ---------- | ||
| days : int, default None | ||
| weeks : int, default None | ||
| hours : int, default None | ||
| minutes : int, default None | ||
| seconds : int, default None | ||
| milliseconds : int, default None | ||
| microseconds : int, default None | ||
| nanoseconds : int, default None | ||
| Notes | ||
| ----- | ||
| For potentially non-fixed-length timedeltas (like year, month, etc.), use | ||
| the corresponding named API (e.g. ibis.month). | ||
| Returns | ||
| ------- | ||
| delta : TimeIncrement (Timedelta) | ||
| """ | ||
| out = { | ||
| 'result': None | ||
| } | ||
|
|
||
| def _apply(klass, n): | ||
| if not n: | ||
| return | ||
| offset = klass(n) | ||
| delta = out['result'] | ||
| out['result'] = delta + offset if delta else offset | ||
|
|
||
| _apply(Week, weeks) | ||
| _apply(Day, days) | ||
| _apply(Hour, hours) | ||
| _apply(Minute, minutes) | ||
| _apply(Second, seconds) | ||
| _apply(Millisecond, milliseconds) | ||
| _apply(Microsecond, microseconds) | ||
| _apply(Nanosecond, nanoseconds) | ||
|
|
||
| result = out['result'] | ||
| if not result: | ||
| raise IbisError('Must pass some offset parameter') | ||
|
|
||
| return result |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| # Copyright 2014 Cloudera Inc. | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| import unittest | ||
|
|
||
| from ibis.expr.tests.mocks import MockConnection | ||
| import ibis.expr.types as ir | ||
|
|
||
|
|
||
| class TestAnalytics(unittest.TestCase): | ||
|
|
||
| def setUp(self): | ||
| self.con = MockConnection() | ||
| self.alltypes = self.con.table('functional_alltypes') | ||
|
|
||
| def test_category_project(self): | ||
| t = self.alltypes | ||
|
|
||
| tier = t.double_col.bucket([0, 50, 100]).name('tier') | ||
| expr = t[tier, t] | ||
|
|
||
| assert isinstance(expr.tier, ir.CategoryArray) | ||
|
|
||
| def test_bucket(self): | ||
| d = self.alltypes.double_col | ||
| bins = [0, 10, 50, 100] | ||
|
|
||
| expr = d.bucket(bins) | ||
| assert isinstance(expr, ir.CategoryArray) | ||
| assert expr.op().nbuckets == 3 | ||
|
|
||
| expr = d.bucket(bins, include_over=True) | ||
| assert expr.op().nbuckets == 4 | ||
|
|
||
| expr = d.bucket(bins, include_over=True, include_under=True) | ||
| assert expr.op().nbuckets == 5 | ||
|
|
||
| def test_bucket_error_cases(self): | ||
| d = self.alltypes.double_col | ||
|
|
||
| self.assertRaises(ValueError, d.bucket, []) | ||
| self.assertRaises(ValueError, d.bucket, [1, 2], closed='foo') | ||
|
|
||
| # it works! | ||
| d.bucket([10], include_under=True, include_over=True) | ||
|
|
||
| self.assertRaises(ValueError, d.bucket, [10]) | ||
| self.assertRaises(ValueError, d.bucket, [10], include_under=True) | ||
| self.assertRaises(ValueError, d.bucket, [10], include_over=True) | ||
|
|
||
| def test_histogram(self): | ||
| d = self.alltypes.double_col | ||
|
|
||
| self.assertRaises(ValueError, d.histogram, nbins=10, binwidth=5) | ||
| self.assertRaises(ValueError, d.histogram) | ||
| self.assertRaises(ValueError, d.histogram, 10, closed='foo') |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,182 @@ | ||
| # Copyright 2014 Cloudera Inc. | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| import unittest | ||
|
|
||
| from ibis.common import IbisError | ||
| import ibis.expr.operations as ops | ||
| import ibis.expr.types as ir | ||
| import ibis.expr.temporal as T | ||
|
|
||
| from ibis.expr.tests.mocks import MockConnection | ||
|
|
||
|
|
||
| class TestFixedOffsets(unittest.TestCase): | ||
|
|
||
| def setUp(self): | ||
| self.con = MockConnection() | ||
| self.table = self.con.table('alltypes') | ||
|
|
||
| def test_upconvert(self): | ||
| cases = [ | ||
| (T.day(14), 'w', T.week(2)), | ||
| (T.hour(72), 'd', T.day(3)), | ||
| (T.minute(240), 'h', T.hour(4)), | ||
| (T.second(360), 'm', T.minute(6)), | ||
| (T.second(3 * 86400), 'd', T.day(3)), | ||
| (T.millisecond(5000), 's', T.second(5)), | ||
| (T.microsecond(5000000), 's', T.second(5)), | ||
| (T.nanosecond(5000000000), 's', T.second(5)), | ||
| ] | ||
|
|
||
| for offset, unit, expected in cases: | ||
| result = offset.to_unit(unit) | ||
| assert result.equals(expected) | ||
|
|
||
| def test_multiply(self): | ||
| offset = T.day(2) | ||
|
|
||
| assert (offset * 2).equals(T.day(4)) | ||
| assert (offset * (-2)).equals(T.day(-4)) | ||
| assert (3 * offset).equals(T.day(6)) | ||
| assert ((-3) * offset).equals(T.day(-6)) | ||
|
|
||
| def test_repr(self): | ||
| assert repr(T.day()) == '<Timedelta: 1 day>' | ||
| assert repr(T.day(2)) == '<Timedelta: 2 days>' | ||
| assert repr(T.year()) == '<Timedelta: 1 year>' | ||
| assert repr(T.month(2)) == '<Timedelta: 2 months>' | ||
| assert repr(T.second(40)) == '<Timedelta: 40 seconds>' | ||
|
|
||
| def test_cannot_upconvert(self): | ||
| cases = [ | ||
| (T.day(), 'w'), | ||
| (T.hour(), 'd'), | ||
| (T.minute(), 'h'), | ||
| (T.second(), 'm'), | ||
| (T.second(), 'd'), | ||
| (T.millisecond(), 's'), | ||
| (T.microsecond(), 's'), | ||
| (T.nanosecond(), 's'), | ||
| ] | ||
|
|
||
| for delta, target in cases: | ||
| self.assertRaises(IbisError, delta.to_unit, target) | ||
|
|
||
| def test_downconvert_second_parts(self): | ||
| K = 2 | ||
|
|
||
| sec = T.second(K) | ||
| milli = T.millisecond(K) | ||
| micro = T.microsecond(K) | ||
| nano = T.nanosecond(K) | ||
|
|
||
| cases = [ | ||
| (sec.to_unit('s'), T.second(K)), | ||
| (sec.to_unit('ms'), T.millisecond(K * 1000)), | ||
| (sec.to_unit('us'), T.microsecond(K * 1000000)), | ||
| (sec.to_unit('ns'), T.nanosecond(K * 1000000000)), | ||
|
|
||
| (milli.to_unit('ms'), T.millisecond(K)), | ||
| (milli.to_unit('us'), T.microsecond(K * 1000)), | ||
| (milli.to_unit('ns'), T.nanosecond(K * 1000000)), | ||
|
|
||
| (micro.to_unit('us'), T.microsecond(K)), | ||
| (micro.to_unit('ns'), T.nanosecond(K * 1000)), | ||
|
|
||
| (nano.to_unit('ns'), T.nanosecond(K)) | ||
| ] | ||
| self._check_cases(cases) | ||
|
|
||
| def test_downconvert_hours(self): | ||
| K = 2 | ||
| offset = T.hour(K) | ||
|
|
||
| cases = [ | ||
| (offset.to_unit('h'), T.hour(K)), | ||
| (offset.to_unit('m'), T.minute(K * 60)), | ||
| (offset.to_unit('s'), T.second(K * 3600)), | ||
| (offset.to_unit('ms'), T.millisecond(K * 3600000)), | ||
| (offset.to_unit('us'), T.microsecond(K * 3600000000)), | ||
| (offset.to_unit('ns'), T.nanosecond(K * 3600000000000L)) | ||
| ] | ||
| self._check_cases(cases) | ||
|
|
||
| def test_downconvert_day(self): | ||
| K = 2 | ||
|
|
||
| week = T.week(K) | ||
| day = T.day(K) | ||
|
|
||
| cases = [ | ||
| (week.to_unit('d'), T.day(K * 7)), | ||
| (week.to_unit('h'), T.hour(K * 7 * 24)), | ||
|
|
||
| (day.to_unit('d'), T.day(K)), | ||
| (day.to_unit('h'), T.hour(K * 24)), | ||
| (day.to_unit('m'), T.minute(K * 1440)), | ||
| (day.to_unit('s'), T.second(K * 86400)), | ||
| (day.to_unit('ms'), T.millisecond(K * 86400000)), | ||
| (day.to_unit('us'), T.microsecond(K * 86400000000)), | ||
| (day.to_unit('ns'), T.nanosecond(K * 86400000000000L)) | ||
| ] | ||
| self._check_cases(cases) | ||
|
|
||
| def test_combine_with_different_kinds(self): | ||
| cases = [ | ||
| (T.day() + T.minute(), T.minute(1441)), | ||
| (T.second() + T.millisecond(10), T.millisecond(1010)), | ||
| (T.hour() + T.minute(5) + T.second(10), T.second(3910)) | ||
| ] | ||
| self._check_cases(cases) | ||
|
|
||
| def test_timedelta_generic_api(self): | ||
| cases = [ | ||
| (T.timedelta(weeks=2), T.week(2)), | ||
| (T.timedelta(days=3), T.day(3)), | ||
| (T.timedelta(hours=4), T.hour(4)), | ||
| (T.timedelta(minutes=5), T.minute(5)), | ||
| (T.timedelta(seconds=6), T.second(6)), | ||
| (T.timedelta(milliseconds=7), T.millisecond(7)), | ||
| (T.timedelta(microseconds=8), T.microsecond(8)), | ||
| (T.timedelta(nanoseconds=9), T.nanosecond(9)), | ||
| ] | ||
| self._check_cases(cases) | ||
|
|
||
| def _check_cases(self, cases): | ||
| for x, y in cases: | ||
| assert x.equals(y) | ||
|
|
||
| def test_offset_timestamp_expr(self): | ||
| c = self.table.i | ||
| x = T.timedelta(days=1) | ||
|
|
||
| expr = x + c | ||
| assert isinstance(expr, ir.TimestampArray) | ||
| assert isinstance(expr.op(), ops.TimestampDelta) | ||
|
|
||
| # test radd | ||
| expr = c + x | ||
| assert isinstance(expr, ir.TimestampArray) | ||
| assert isinstance(expr.op(), ops.TimestampDelta) | ||
|
|
||
|
|
||
| class TestTimedelta(unittest.TestCase): | ||
|
|
||
| def test_compound_offset(self): | ||
| # These are not yet allowed (e.g. 1 month + 1 hour) | ||
| pass | ||
|
|
||
| def test_offset_months(self): | ||
| pass |