Skip to content

Commit

Permalink
Merge pull request #3849 from jitseniesen/datetimeindex
Browse files Browse the repository at this point in the history
PR: Support DatetimeIndex in Variable Explorer
  • Loading branch information
ccordoba12 authored Jan 8, 2017
2 parents b0d4ee5 + 7581d1d commit 41954c5
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 15 deletions.
2 changes: 1 addition & 1 deletion doc/variableexplorer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Supported types
The variable explorer can't show all types of objects. The ones currently
supported are:

#. `Pandas` DataFrames and TimeSeries
#. `Pandas` DataFrame, TimeSeries and DatetimeIndex objects
#. `NumPy` arrays and matrices
#. `PIL/Pillow` images
#. `datetime` dates
Expand Down
4 changes: 2 additions & 2 deletions spyder/config/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,8 +383,8 @@ def get_supported_types():
except ImportError:
pass
try:
from pandas import DataFrame, Series
editable_types += [DataFrame, Series]
from pandas import DataFrame, Series, DatetimeIndex
editable_types += [DataFrame, Series, DatetimeIndex]
except ImportError:
pass
picklable_types = editable_types[:]
Expand Down
11 changes: 6 additions & 5 deletions spyder/widgets/variableexplorer/collectionseditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,11 @@
from spyder.widgets.variableexplorer.importwizard import ImportWizard
from spyder.widgets.variableexplorer.texteditor import TextEditor
from spyder.widgets.variableexplorer.utils import (
array, DataFrame, display_to_value, FakeObject, get_color_name,
get_human_readable_type, get_size, Image, is_editable_type, is_known_type,
MaskedArray, ndarray, np_savetxt, Series, sort_against, try_to_eval,
unsorted_unique, value_to_display, get_object_attrs, get_type_string)
array, DataFrame, DatetimeIndex, display_to_value, FakeObject,
get_color_name, get_human_readable_type, get_size, Image, is_editable_type,
is_known_type, MaskedArray, ndarray, np_savetxt, Series, sort_against,
try_to_eval, unsorted_unique, value_to_display, get_object_attrs,
get_type_string)

if ndarray is not FakeObject:
from spyder.widgets.variableexplorer.arrayeditor import ArrayEditor
Expand Down Expand Up @@ -469,7 +470,7 @@ def createEditor(self, parent, option, index):
conv=conv_func))
return None
#--editor = DataFrameEditor
elif isinstance(value, (DataFrame, Series)) \
elif isinstance(value, (DataFrame, DatetimeIndex, Series)) \
and DataFrame is not FakeObject:
editor = DataFrameEditor()
if not editor.setup_and_check(value, title=key):
Expand Down
7 changes: 5 additions & 2 deletions spyder/widgets/variableexplorer/dataframeeditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"""

# Third party imports
from pandas import DataFrame, Series
from pandas import DataFrame, DatetimeIndex, Series
from qtpy import API, PYQT5
from qtpy.compat import from_qvariant, to_qvariant
from qtpy.QtCore import QAbstractTableModel, QModelIndex, Qt, Signal, Slot
Expand Down Expand Up @@ -648,7 +648,8 @@ def __init__(self, parent=None):
def setup_and_check(self, data, title=''):
"""
Setup DataFrameEditor:
return False if data is not supported, True otherwise
return False if data is not supported, True otherwise.
Supported types for data are DataFrame, Series and DatetimeIndex.
"""
self.layout = QGridLayout()
self.setLayout(self.layout)
Expand All @@ -660,6 +661,8 @@ def setup_and_check(self, data, title=''):
if isinstance(data, Series):
self.is_series = True
data = data.to_frame()
elif isinstance(data, DatetimeIndex):
data = DataFrame(data)

self.setWindowTitle(title)
self.resize(600, 500)
Expand Down
48 changes: 47 additions & 1 deletion spyder/widgets/variableexplorer/tests/test_collectioneditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@

# Local imports
from spyder.widgets.variableexplorer.collectionseditor import (
CollectionsEditorTableView)
CollectionsEditorTableView, CollectionsModel)

# Helper functions
def data(cm, i, j):
return cm.data(cm.createIndex(i, j))

# --- Tests
# -----------------------------------------------------------------------------
Expand Down Expand Up @@ -48,6 +52,48 @@ def test_accept_sig_option_changed_from_dataframeeditor(qtbot, monkeypatch):
dataframe_editor.sig_option_changed.emit('dataframe_format', '%5f')
assert editor.model.dataframe_format == '%5f'

def test_collectionsmodel_with_two_ints():
coll = {'x': 1, 'y': 2}
cm = CollectionsModel(None, coll)
assert cm.rowCount() == 2
assert cm.columnCount() == 4
# dict is unordered, so first row might be x or y
assert data(cm, 0, 0) in {'x', 'y'}
if data(cm, 0, 0) == 'x':
row_with_x = 0
row_with_y = 1
else:
row_with_x = 1
row_with_y = 0
assert data(cm, row_with_x, 1) == 'int'
assert data(cm, row_with_x, 2) == '1'
assert data(cm, row_with_x, 3) == '1'
assert data(cm, row_with_y, 0) == 'y'
assert data(cm, row_with_y, 1) == 'int'
assert data(cm, row_with_y, 2) == '1'
assert data(cm, row_with_y, 3) == '2'

def test_collectionsmodel_with_datetimeindex():
# Regression test for issue #3380
rng = pandas.date_range('10/1/2016', periods=25, freq='bq')
coll = {'rng': rng}
cm = CollectionsModel(None, coll)
assert data(cm, 0, 0) == 'rng'
assert data(cm, 0, 1) == 'DatetimeIndex'
assert data(cm, 0, 2) == '(25,)' or data(cm, 0, 2) == '(25L,)'
assert data(cm, 0, 3) == rng.summary()

def test_shows_dataframeeditor_when_editing_datetimeindex(qtbot, monkeypatch):
MockDataFrameEditor = Mock()
mockDataFrameEditor_instance = MockDataFrameEditor()
monkeypatch.setattr('spyder.widgets.variableexplorer.collectionseditor.DataFrameEditor',
MockDataFrameEditor)
rng = pandas.date_range('10/1/2016', periods=25, freq='bq')
coll = {'rng': rng}
editor = CollectionsEditorTableView(None, coll)
editor.delegate.createEditor(None, None, editor.model.createIndex(0, 3))
mockDataFrameEditor_instance.show.assert_called_once_with()


if __name__ == "__main__":
pytest.main()
11 changes: 11 additions & 0 deletions spyder/widgets/variableexplorer/tests/test_dataframeeditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,17 @@ def test_header_encoding():
assert model.headerData(5, orientation=Qt.Horizontal) == "c"
assert model.headerData(6, orientation=Qt.Horizontal) == "d"

def test_dataframeeditor_with_datetimeindex():
rng = date_range('20150101', periods=3)
editor = DataFrameEditor(None)
editor.setup_and_check(rng)
dfm = editor.dataModel
assert dfm.rowCount() == 3
assert dfm.columnCount() == 2
assert data(dfm, 0, 1) == '2015-01-01 00:00:00'
assert data(dfm, 1, 1) == '2015-01-02 00:00:00'
assert data(dfm, 2, 1) == '2015-01-03 00:00:00'


if __name__ == "__main__":
pytest.main()
13 changes: 9 additions & 4 deletions spyder/widgets/variableexplorer/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,9 @@ def get_numpy_dtype(obj):
# Pandas support
#==============================================================================
if programs.is_module_installed('pandas', PANDAS_REQVER):
from pandas import DataFrame, Series
from pandas import DataFrame, DatetimeIndex, Series
else:
DataFrame = Series = FakeObject # analysis:ignore
DataFrame = DatetimeIndex = Series = FakeObject # analysis:ignore


#==============================================================================
Expand Down Expand Up @@ -132,7 +132,7 @@ def get_size(item):
return item.shape
elif isinstance(item, Image):
return item.size
if isinstance(item, (DataFrame, Series)):
if isinstance(item, (DataFrame, DatetimeIndex, Series)):
return item.shape
else:
return 1
Expand Down Expand Up @@ -201,7 +201,8 @@ def datestr_to_datetime(value):
MaskedArray,
matrix,
DataFrame,
Series): ARRAY_COLOR,
Series,
DatetimeIndex): ARRAY_COLOR,
Image: "#008000",
datetime.date: "#808000",
}
Expand Down Expand Up @@ -297,6 +298,8 @@ def value_to_display(value, minmax=False):
elif isinstance(value, NavigableString):
# Fixes Issue 2448
display = to_text_string(value)
elif isinstance(value, DatetimeIndex):
display = value.summary()
elif is_binary_string(value):
try:
display = to_text_string(value, 'utf8')
Expand Down Expand Up @@ -381,6 +384,8 @@ def get_type_string(item):
"""Return type string of an object."""
if isinstance(item, DataFrame):
return "DataFrame"
if isinstance(item, DatetimeIndex):
return "DatetimeIndex"
if isinstance(item, Series):
return "Series"
found = re.findall(r"<(?:type|class) '(\S*)'>",
Expand Down

0 comments on commit 41954c5

Please sign in to comment.