Skip to content

Instantly share code, notes, and snippets.

@shoyer
Last active August 29, 2015 14:00
Show Gist options
  • Save shoyer/497789ebabb3a82411b2 to your computer and use it in GitHub Desktop.
Save shoyer/497789ebabb3a82411b2 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"metadata": {
"name": "",
"signature": "sha256:8170a590ec873beee1a518b732269eb6afc522bfd7b834ab58617d2c12557d7f"
},
"nbformat": 3,
"nbformat_minor": 0,
"worksheets": [
{
"cells": [
{
"cell_type": "heading",
"level": 1,
"metadata": {},
"source": [
"xray: extended arrays for scientific datasets"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Stephan Hoyer [(shoyer)](https://twitter.com/shoyer)\n",
"\n",
"#### [PyData Silicon Valley, May 3, 2014](http://pydata.org/sv2014/)\n",
"\n",
"GitHub: [github.com/xray/xray](http://github.com/xray/xray) <br />\n",
"Docs: [xray.readthedocs.org](http://xray.readthedocs.org) <br />\n",
"\n",
"---------------------"
]
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"Setup/imports"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"%matplotlib inline"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 1
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"import numpy as np\n",
"import pandas as pd\n",
"import xray"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 2
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"xray.__version__"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 3,
"text": [
"'0.1.0'"
]
}
],
"prompt_number": 3
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"Creating a Dataset"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To make an `xray.Dataset` from scratch, pass in a dictionary with values in the form `(dimensions, data[, attributes])`.\n",
"\n",
"You can also pass in `xray.Variable` or `xray.DataArray` objects directly."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"foo_values = np.random.RandomState(0).rand(3, 4)\n",
"times = pd.date_range('2000-01-01', periods=3)\n",
"\n",
"ds = xray.Dataset({'time': ('time', times),\n",
" 'foo': (['time', 'space'], foo_values)})\n",
"ds"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 4,
"text": [
"<xray.Dataset>\n",
"Dimensions: (space: 4, time: 3)\n",
"Coordinates:\n",
" space X \n",
" time X \n",
"Noncoordinates:\n",
" foo 1 0 \n",
"Attributes:\n",
" Empty"
]
}
],
"prompt_number": 4
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"ds['numbers'] = ('space', [10, 10, 20, 20])\n",
"ds['abc'] = ('time', ['A', 'B', 'C'])\n",
"ds"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 5,
"text": [
"<xray.Dataset>\n",
"Dimensions: (space: 4, time: 3)\n",
"Coordinates:\n",
" space X \n",
" time X \n",
"Noncoordinates:\n",
" foo 1 0 \n",
" numbers 0 \n",
" abc 0 \n",
"Attributes:\n",
" Empty"
]
}
],
"prompt_number": 5
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"'foo' in ds"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 6,
"text": [
"True"
]
}
],
"prompt_number": 6
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"ds.keys()"
],
"language": "python",
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 7,
"text": [
"['foo', 'time', 'space', 'numbers', 'abc']"
]
}
],
"prompt_number": 7
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"ds['time']"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 8,
"text": [
"<xray.DataArray 'time' (time: 3)>\n",
"array(['1999-12-31T16:00:00.000000000-0800',\n",
" '2000-01-01T16:00:00.000000000-0800',\n",
" '2000-01-02T16:00:00.000000000-0800'], dtype='datetime64[ns]')\n",
"Attributes:\n",
" Empty"
]
}
],
"prompt_number": 8
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"ds['space']"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 9,
"text": [
"<xray.DataArray 'space' (space: 4)>\n",
"array([0, 1, 2, 3])\n",
"Attributes:\n",
" Empty"
]
}
],
"prompt_number": 9
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"ds['foo']"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 10,
"text": [
"<xray.DataArray 'foo' (time: 3, space: 4)>\n",
"array([[ 0.5488135 , 0.71518937, 0.60276338, 0.54488318],\n",
" [ 0.4236548 , 0.64589411, 0.43758721, 0.891773 ],\n",
" [ 0.96366276, 0.38344152, 0.79172504, 0.52889492]])\n",
"Attributes:\n",
" Empty"
]
}
],
"prompt_number": 10
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`xray.Dataset` is dict-like, so it also has `values`, `items`, `__del__`, `get`, `update` etc."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"***attributes*: a dictionary for metadata**"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"ds.attrs"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 11,
"text": [
"OrderedDict()"
]
}
],
"prompt_number": 11
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"ds.attrs['title'] = 'example attribute'\n",
"ds"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 12,
"text": [
"<xray.Dataset>\n",
"Dimensions: (space: 4, time: 3)\n",
"Coordinates:\n",
" space X \n",
" time X \n",
"Noncoordinates:\n",
" foo 1 0 \n",
" numbers 0 \n",
" abc 0 \n",
"Attributes:\n",
" title: example attribute"
]
}
],
"prompt_number": 12
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"DataArray object"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The contents of `Dataset`s are `DataArray`s:\n",
"\n",
"- labeled dimensions (axes)\n",
"- labeled coordinates (indices)\n",
"- data with homogeneous dtype\n",
"- attributes (metadata)"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"foo = ds['foo']\n",
"foo"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 13,
"text": [
"<xray.DataArray 'foo' (time: 3, space: 4)>\n",
"array([[ 0.5488135 , 0.71518937, 0.60276338, 0.54488318],\n",
" [ 0.4236548 , 0.64589411, 0.43758721, 0.891773 ],\n",
" [ 0.96366276, 0.38344152, 0.79172504, 0.52889492]])\n",
"Attributes:\n",
" Empty"
]
}
],
"prompt_number": 13
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Like pandas objects, they can be thought of as fancy wrapper around a numpy array:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"foo.values"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 14,
"text": [
"array([[ 0.5488135 , 0.71518937, 0.60276338, 0.54488318],\n",
" [ 0.4236548 , 0.64589411, 0.43758721, 0.891773 ],\n",
" [ 0.96366276, 0.38344152, 0.79172504, 0.52889492]])"
]
}
],
"prompt_number": 14
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"They also have dimension labels:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"foo.dimensions"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 15,
"text": [
"('time', 'space')"
]
}
],
"prompt_number": 15
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"And their own attributes:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"foo.attrs"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 16,
"text": [
"OrderedDict()"
]
}
],
"prompt_number": 16
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"Indexing"
]
},
{
"cell_type": "heading",
"level": 4,
"metadata": {},
"source": [
"Normal indexing works (mostly) like numpy:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"foo[0]"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 17,
"text": [
"<xray.DataArray 'foo' (space: 4)>\n",
"array([ 0.5488135 , 0.71518937, 0.60276338, 0.54488318])\n",
"Attributes:\n",
" Empty"
]
}
],
"prompt_number": 17
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"foo[:2]"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 18,
"text": [
"<xray.DataArray 'foo' (time: 2, space: 4)>\n",
"array([[ 0.5488135 , 0.71518937, 0.60276338, 0.54488318],\n",
" [ 0.4236548 , 0.64589411, 0.43758721, 0.891773 ]])\n",
"Attributes:\n",
" Empty"
]
}
],
"prompt_number": 18
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"foo[:, [2, 1]]"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 19,
"text": [
"<xray.DataArray 'foo' (time: 3, space: 2)>\n",
"array([[ 0.60276338, 0.71518937],\n",
" [ 0.43758721, 0.64589411],\n",
" [ 0.79172504, 0.38344152]])\n",
"Attributes:\n",
" Empty"
]
}
],
"prompt_number": 19
},
{
"cell_type": "heading",
"level": 4,
"metadata": {},
"source": [
"Location based indexing like pandas:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"foo.loc['2000-01-01':'2000-01-02', 0]"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 20,
"text": [
"<xray.DataArray 'foo' (time: 2)>\n",
"array([ 0.5488135, 0.4236548])\n",
"Attributes:\n",
" Empty"
]
}
],
"prompt_number": 20
},
{
"cell_type": "heading",
"level": 4,
"metadata": {},
"source": [
"Use dimension names explicitly with `indexed` or `labeled`:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"# index by array indices\n",
"foo.indexed(space=0, time=slice(0, 2))"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 21,
"text": [
"<xray.DataArray 'foo' (time: 2)>\n",
"array([ 0.5488135, 0.4236548])\n",
"Attributes:\n",
" Empty"
]
}
],
"prompt_number": 21
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"# index by coordinate labels\n",
"foo.labeled(space=0, time=slice('2000-01-01', '2000-01-02'))"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 22,
"text": [
"<xray.DataArray 'foo' (time: 2)>\n",
"array([ 0.5488135, 0.4236548])\n",
"Attributes:\n",
" Empty"
]
}
],
"prompt_number": 22
},
{
"cell_type": "heading",
"level": 4,
"metadata": {},
"source": [
"Indexing Datasets"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"ds.indexed(space=[0], time=[0])"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 23,
"text": [
"<xray.Dataset>\n",
"Dimensions: (space: 1, time: 1)\n",
"Coordinates:\n",
" space X \n",
" time X \n",
"Noncoordinates:\n",
" foo 1 0 \n",
" numbers 0 \n",
" abc 0 \n",
"Attributes:\n",
" title: example attribute"
]
}
],
"prompt_number": 23
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"ds.labeled(time='2000-01-01')"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 24,
"text": [
"<xray.Dataset>\n",
"Dimensions: (space: 4)\n",
"Coordinates:\n",
" space X \n",
"Noncoordinates:\n",
" foo 0 \n",
" time \n",
" numbers 0 \n",
" abc \n",
"Attributes:\n",
" title: example attribute"
]
}
],
"prompt_number": 24
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"Combining data"
]
},
{
"cell_type": "heading",
"level": 4,
"metadata": {},
"source": [
"Concatenating"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"xray.DataArray.concat([foo[0], foo[1]], 'new_dim')"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 25,
"text": [
"<xray.DataArray 'foo' (new_dim: 2, space: 4)>\n",
"array([[ 0.5488135 , 0.71518937, 0.60276338, 0.54488318],\n",
" [ 0.4236548 , 0.64589411, 0.43758721, 0.891773 ]])\n",
"Attributes:\n",
" Empty"
]
}
],
"prompt_number": 25
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"xray.Dataset.concat([ds.labeled(time='2000-01-01'),\n",
" ds.labeled(time='2000-01-03')],\n",
" 'new_dim')"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 26,
"text": [
"<xray.Dataset>\n",
"Dimensions: (new_dim: 2, space: 4)\n",
"Coordinates:\n",
" new_dim X \n",
" space X \n",
"Noncoordinates:\n",
" numbers 0 \n",
" foo 0 1 \n",
" abc 0 \n",
" time 0 \n",
"Attributes:\n",
" title: example attribute"
]
}
],
"prompt_number": 26
},
{
"cell_type": "heading",
"level": 4,
"metadata": {},
"source": [
"Merging"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"ds.merge({'hello': ('space', np.arange(4) + 10)})"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 27,
"text": [
"<xray.Dataset>\n",
"Dimensions: (space: 4, time: 3)\n",
"Coordinates:\n",
" space X \n",
" time X \n",
"Noncoordinates:\n",
" foo 1 0 \n",
" numbers 0 \n",
" abc 0 \n",
" hello 0 \n",
"Attributes:\n",
" title: example attribute"
]
}
],
"prompt_number": 27
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"`DataArray` math"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Basic math like numpy or pandas:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"foo - 3"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 28,
"text": [
"<xray.DataArray 'foo___sub___other' (time: 3, space: 4)>\n",
"array([[-2.4511865 , -2.28481063, -2.39723662, -2.45511682],\n",
" [-2.5763452 , -2.35410589, -2.56241279, -2.108227 ],\n",
" [-2.03633724, -2.61655848, -2.20827496, -2.47110508]])\n",
"Attributes:\n",
" Empty"
]
}
],
"prompt_number": 28
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"np.sin(foo)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 29,
"text": [
"<xray.DataArray 'foo_' (time: 3, space: 4)>\n",
"array([[ 0.52167534, 0.65576039, 0.56692103, 0.51831819],\n",
" [ 0.41109488, 0.60191268, 0.42375525, 0.77818647],\n",
" [ 0.82128673, 0.37411428, 0.71156637, 0.50457956]])\n",
"Attributes:\n",
" Empty"
]
}
],
"prompt_number": 29
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"foo.T"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 30,
"text": [
"<xray.DataArray 'foo' (space: 4, time: 3)>\n",
"array([[ 0.5488135 , 0.4236548 , 0.96366276],\n",
" [ 0.71518937, 0.64589411, 0.38344152],\n",
" [ 0.60276338, 0.43758721, 0.79172504],\n",
" [ 0.54488318, 0.891773 , 0.52889492]])\n",
"Attributes:\n",
" Empty"
]
}
],
"prompt_number": 30
},
{
"cell_type": "heading",
"level": 4,
"metadata": {},
"source": [
"Aggregate by dimension name, not axis number:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"foo.sum('time')"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 31,
"text": [
"<xray.DataArray 'foo' (space: 4)>\n",
"array([ 1.93613106, 1.744525 , 1.83207563, 1.9655511 ])\n",
"Attributes:\n",
" Empty"
]
}
],
"prompt_number": 31
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"foo.std(['time', 'space'])"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 32,
"text": [
"<xray.DataArray 'foo' ()>\n",
"array(0.17751915478163727)\n",
"Attributes:\n",
" Empty"
]
}
],
"prompt_number": 32
},
{
"cell_type": "heading",
"level": 4,
"metadata": {},
"source": [
"Math alignment (broadcasting) based on dimension names"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- Operations vectorize across missing dimensions\n",
"- Dimensions are reordered if necessary"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"foo[:, 0]"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 33,
"text": [
"<xray.DataArray 'foo' (time: 3)>\n",
"array([ 0.5488135 , 0.4236548 , 0.96366276])\n",
"Attributes:\n",
" Empty"
]
}
],
"prompt_number": 33
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"foo[0, :]"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 34,
"text": [
"<xray.DataArray 'foo' (space: 4)>\n",
"array([ 0.5488135 , 0.71518937, 0.60276338, 0.54488318])\n",
"Attributes:\n",
" Empty"
]
}
],
"prompt_number": 34
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"foo[:, 0] * foo[0, :]"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 35,
"text": [
"<xray.DataArray 'foo___mul___foo' (time: 3, space: 4)>\n",
"array([[ 0.30119626, 0.39250558, 0.33080468, 0.29903925],\n",
" [ 0.23250747, 0.30299341, 0.2553636 , 0.23084238],\n",
" [ 0.52887114, 0.68920136, 0.58086062, 0.52508363]])\n",
"Attributes:\n",
" Empty"
]
}
],
"prompt_number": 35
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"foo - foo.T"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 36,
"text": [
"<xray.DataArray 'foo___sub___foo' (time: 3, space: 4)>\n",
"array([[ 0., 0., 0., 0.],\n",
" [ 0., 0., 0., 0.],\n",
" [ 0., 0., 0., 0.]])\n",
"Attributes:\n",
" Empty"
]
}
],
"prompt_number": 36
},
{
"cell_type": "heading",
"level": 4,
"metadata": {},
"source": [
"Alignment on coordinate values (like pandas)"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"bar = (10 * foo[:2, :2]).rename('bar')\n",
"bar"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 37,
"text": [
"<xray.DataArray 'bar' (time: 2, space: 2)>\n",
"array([[ 5.48813504, 7.15189366],\n",
" [ 4.23654799, 6.45894113]])\n",
"Attributes:\n",
" Empty"
]
}
],
"prompt_number": 37
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"foo.reindex_like(bar)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 38,
"text": [
"<xray.DataArray 'foo' (time: 2, space: 2)>\n",
"array([[ 0.5488135 , 0.71518937],\n",
" [ 0.4236548 , 0.64589411]])\n",
"Attributes:\n",
" Empty"
]
}
],
"prompt_number": 38
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"bar.reindex_like(foo)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 39,
"text": [
"<xray.DataArray 'bar' (time: 3, space: 4)>\n",
"array([[ 5.48813504, 7.15189366, nan, nan],\n",
" [ 4.23654799, 6.45894113, nan, nan],\n",
" [ nan, nan, nan, nan]])\n",
"Attributes:\n",
" Empty"
]
}
],
"prompt_number": 39
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"xray.align(foo, bar, join='inner')"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 40,
"text": [
"(<xray.DataArray 'foo' (time: 2, space: 2)>\n",
" array([[ 0.5488135 , 0.71518937],\n",
" [ 0.4236548 , 0.64589411]])\n",
" Attributes:\n",
" Empty, <xray.DataArray 'bar' (time: 2, space: 2)>\n",
" array([[ 5.48813504, 7.15189366],\n",
" [ 4.23654799, 6.45894113]])\n",
" Attributes:\n",
" Empty)"
]
}
],
"prompt_number": 40
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"ds.reindex_like(bar)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 41,
"text": [
"<xray.Dataset>\n",
"Dimensions: (space: 2, time: 2)\n",
"Coordinates:\n",
" space X \n",
" time X \n",
"Noncoordinates:\n",
" foo 1 0 \n",
" numbers 0 \n",
" abc 0 \n",
"Attributes:\n",
" title: example attribute"
]
}
],
"prompt_number": 41
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"Transform to and from pandas objects"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"* `xray.Dataset` &#8596; `pandas.DataFrame` with a `MultiIndex`\n",
"* `xray.DataArray` &#8596; `pandas.Series` with a `MultiIndex`\n",
"\n",
"Think Hadley Wickham's [\"tidy data\"](http://vita.had.co.nz/papers/tidy-data.pdf)"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"ds"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 42,
"text": [
"<xray.Dataset>\n",
"Dimensions: (space: 4, time: 3)\n",
"Coordinates:\n",
" space X \n",
" time X \n",
"Noncoordinates:\n",
" foo 1 0 \n",
" numbers 0 \n",
" abc 0 \n",
"Attributes:\n",
" title: example attribute"
]
}
],
"prompt_number": 42
},
{
"cell_type": "heading",
"level": 4,
"metadata": {},
"source": [
"Convert to/from a dataframe:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"df = ds.to_dataframe()\n",
"df"
],
"language": "python",
"metadata": {},
"outputs": [
{
"html": [
"<div style=\"max-height:1000px;max-width:1500px;overflow:auto;\">\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th></th>\n",
" <th>foo</th>\n",
" <th>numbers</th>\n",
" <th>abc</th>\n",
" </tr>\n",
" <tr>\n",
" <th>space</th>\n",
" <th>time</th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th rowspan=\"3\" valign=\"top\">0</th>\n",
" <th>2000-01-01</th>\n",
" <td> 0.548814</td>\n",
" <td> 10</td>\n",
" <td> A</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2000-01-02</th>\n",
" <td> 0.423655</td>\n",
" <td> 10</td>\n",
" <td> B</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2000-01-03</th>\n",
" <td> 0.963663</td>\n",
" <td> 10</td>\n",
" <td> C</td>\n",
" </tr>\n",
" <tr>\n",
" <th rowspan=\"3\" valign=\"top\">1</th>\n",
" <th>2000-01-01</th>\n",
" <td> 0.715189</td>\n",
" <td> 10</td>\n",
" <td> A</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2000-01-02</th>\n",
" <td> 0.645894</td>\n",
" <td> 10</td>\n",
" <td> B</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2000-01-03</th>\n",
" <td> 0.383442</td>\n",
" <td> 10</td>\n",
" <td> C</td>\n",
" </tr>\n",
" <tr>\n",
" <th rowspan=\"3\" valign=\"top\">2</th>\n",
" <th>2000-01-01</th>\n",
" <td> 0.602763</td>\n",
" <td> 20</td>\n",
" <td> A</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2000-01-02</th>\n",
" <td> 0.437587</td>\n",
" <td> 20</td>\n",
" <td> B</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2000-01-03</th>\n",
" <td> 0.791725</td>\n",
" <td> 20</td>\n",
" <td> C</td>\n",
" </tr>\n",
" <tr>\n",
" <th rowspan=\"3\" valign=\"top\">3</th>\n",
" <th>2000-01-01</th>\n",
" <td> 0.544883</td>\n",
" <td> 20</td>\n",
" <td> A</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2000-01-02</th>\n",
" <td> 0.891773</td>\n",
" <td> 20</td>\n",
" <td> B</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2000-01-03</th>\n",
" <td> 0.528895</td>\n",
" <td> 20</td>\n",
" <td> C</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>12 rows \u00d7 3 columns</p>\n",
"</div>"
],
"metadata": {},
"output_type": "pyout",
"prompt_number": 43,
"text": [
" foo numbers abc\n",
"space time \n",
"0 2000-01-01 0.548814 10 A\n",
" 2000-01-02 0.423655 10 B\n",
" 2000-01-03 0.963663 10 C\n",
"1 2000-01-01 0.715189 10 A\n",
" 2000-01-02 0.645894 10 B\n",
" 2000-01-03 0.383442 10 C\n",
"2 2000-01-01 0.602763 20 A\n",
" 2000-01-02 0.437587 20 B\n",
" 2000-01-03 0.791725 20 C\n",
"3 2000-01-01 0.544883 20 A\n",
" 2000-01-02 0.891773 20 B\n",
" 2000-01-03 0.528895 20 C\n",
"\n",
"[12 rows x 3 columns]"
]
}
],
"prompt_number": 43
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"xray.Dataset.from_dataframe(df)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 44,
"text": [
"<xray.Dataset>\n",
"Dimensions: (space: 4, time: 3)\n",
"Coordinates:\n",
" space X \n",
" time X \n",
"Noncoordinates:\n",
" foo 0 1 \n",
" numbers 0 1 \n",
" abc 0 1 \n",
"Attributes:\n",
" Empty"
]
}
],
"prompt_number": 44
},
{
"cell_type": "heading",
"level": 4,
"metadata": {},
"source": [
"Convert to/from a pandas.Series:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"s = foo.to_series()\n",
"s"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 45,
"text": [
"time space\n",
"2000-01-01 0 0.548814\n",
" 1 0.715189\n",
" 2 0.602763\n",
" 3 0.544883\n",
"2000-01-02 0 0.423655\n",
" 1 0.645894\n",
" 2 0.437587\n",
" 3 0.891773\n",
"2000-01-03 0 0.963663\n",
" 1 0.383442\n",
" 2 0.791725\n",
" 3 0.528895\n",
"Name: foo, dtype: float64"
]
}
],
"prompt_number": 45
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"xray.DataArray.from_series(s)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 46,
"text": [
"<xray.DataArray 'foo' (time: 3, space: 4)>\n",
"array([[ 0.5488135 , 0.71518937, 0.60276338, 0.54488318],\n",
" [ 0.4236548 , 0.64589411, 0.43758721, 0.891773 ],\n",
" [ 0.96366276, 0.38344152, 0.79172504, 0.52889492]])\n",
"Attributes:\n",
" Empty"
]
}
],
"prompt_number": 46
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## GroupBy operations like pandas"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"ds['numbers']"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 47,
"text": [
"<xray.DataArray 'numbers' (space: 4)>\n",
"array([10, 10, 20, 20])\n",
"Attributes:\n",
" Empty"
]
}
],
"prompt_number": 47
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"ds.groupby('numbers')"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 48,
"text": [
"<xray.groupby.DatasetGroupBy at 0x106e21a50>"
]
}
],
"prompt_number": 48
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"list(ds.groupby('numbers'))"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 49,
"text": [
"[(10, <xray.Dataset>\n",
" Dimensions: (space: 2, time: 3)\n",
" Coordinates:\n",
" space X \n",
" time X \n",
" Noncoordinates:\n",
" foo 1 0 \n",
" numbers 0 \n",
" abc 0 \n",
" Attributes:\n",
" title: example attribute), (20, <xray.Dataset>\n",
" Dimensions: (space: 2, time: 3)\n",
" Coordinates:\n",
" space X \n",
" time X \n",
" Noncoordinates:\n",
" foo 1 0 \n",
" numbers 0 \n",
" abc 0 \n",
" Attributes:\n",
" title: example attribute)]"
]
}
],
"prompt_number": 49
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"list(foo.groupby('numbers'))"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 50,
"text": [
"[(10, <xray.DataArray 'foo' (time: 3, space: 2)>\n",
" array([[ 0.5488135 , 0.71518937],\n",
" [ 0.4236548 , 0.64589411],\n",
" [ 0.96366276, 0.38344152]])\n",
" Attributes:\n",
" Empty), (20, <xray.DataArray 'foo' (time: 3, space: 2)>\n",
" array([[ 0.60276338, 0.54488318],\n",
" [ 0.43758721, 0.891773 ],\n",
" [ 0.79172504, 0.52889492]])\n",
" Attributes:\n",
" Empty)]"
]
}
],
"prompt_number": 50
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"foo.groupby('numbers').mean()"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 51,
"text": [
"<xray.DataArray 'foo' (numbers: 2)>\n",
"array([ 0.61344268, 0.63293779])\n",
"Attributes:\n",
" Empty"
]
}
],
"prompt_number": 51
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"def standardize(x):\n",
" return (x - np.mean(x)) / np.std(x)"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 52
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"foo.groupby('numbers').apply(standardize)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 53,
"text": [
"<xray.DataArray 'foo___sub___foo___div___foo' (time: 3, space: 4)>\n",
"array([[-0.33215565, 0.52291769, -0.1909289 , -0.55716642],\n",
" [-0.97539723, 0.16678115, -1.23608278, 1.63778246],\n",
" [ 1.7999237 , -1.18206966, 1.00472795, -0.65833232]])\n",
"Attributes:\n",
" Empty"
]
}
],
"prompt_number": 53
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Limitations: it only works for grouping by a single 1D variable (for now)"
]
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"Reading and writing to disk (NetCDF)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### What is NetCDF?\n",
"\n",
"* Fully self-described data, just like `xray.Dataset`\n",
"* Widely used in the geosciences\n",
"* A subset of HDF5 \n",
"* Portable - supported on many platforms\n"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"ds.to_netcdf('saved_on_disk.nc')"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 54
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"ds_disk = xray.open_dataset('saved_on_disk.nc')\n",
"ds_disk"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 55,
"text": [
"<xray.Dataset>\n",
"Dimensions: (space: 4, time: 3)\n",
"Coordinates:\n",
" space X \n",
" time X \n",
"Noncoordinates:\n",
" foo 1 0 \n",
" numbers 0 \n",
" abc 0 \n",
"Attributes:\n",
" title: example attribute"
]
}
],
"prompt_number": 55
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"Working with remote datasets (OpenDAP)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"xray includes support for OpenDAP (via the NetCDF library), which lets us access large datasets over HTTP"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"remote_data = xray.open_dataset(\n",
" 'http://iridl.ldeo.columbia.edu/SOURCES/.OSU/.PRISM/.monthly/dods')\n",
"remote_data"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 56,
"text": [
"<xray.Dataset>\n",
"Dimensions: (T: 1432, X: 1405, Y: 621)\n",
"Coordinates:\n",
" T X \n",
" X X \n",
" Y X \n",
"Noncoordinates:\n",
" ppt 0 2 1 \n",
" tdmean 0 2 1 \n",
" tmax 0 2 1 \n",
" tmin 0 2 1 \n",
"Attributes:\n",
" Conventions: IRIDL\n",
" expires: 1401580800"
]
}
],
"prompt_number": 56
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"remote_data['tmax']"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 57,
"text": [
"<xray.DataArray 'tmax' (T: 1432, Y: 621, X: 1405)>\n",
"[1249427160 values with dtype=float64]\n",
"Attributes:\n",
" pointwidth: 120\n",
" units: Celsius_scale\n",
" missing_value: -9999\n",
" standard_name: air_temperature\n",
" expires: 1401580800"
]
}
],
"prompt_number": 57
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"tmax = remote_data['tmax'][:500, ::3, ::3]\n",
"tmax"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 58,
"text": [
"<xray.DataArray 'tmax' (T: 500, Y: 207, X: 469)>\n",
"[48541500 values with dtype=float64]\n",
"Attributes:\n",
" pointwidth: 120\n",
" units: Celsius_scale\n",
" missing_value: -9999\n",
" standard_name: air_temperature\n",
" expires: 1401580800"
]
}
],
"prompt_number": 58
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"tmax_ss = tmax[0]\n",
"tmax_ss.values[tmax_ss.values < -99] = np.nan"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 59
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"import matplotlib.pyplot as plt\n",
"from matplotlib.cm import get_cmap\n",
"\n",
"plt.figure(figsize=(9, 5))\n",
"plt.gca().patch.set_color('0') \n",
"plt.contourf(tmax_ss['X'], tmax_ss['Y'], tmax_ss.values, 20,\n",
" cmap=get_cmap('RdBu_r'))\n",
"plt.colorbar();"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "display_data",
"png": "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\nsWPlcrn+7wlj6tSp5Z/97Gflcrlc/s53vlO+4447yuWy/N4MDAzk2dTMmDZtWnn9+vXlcrlcXrdu\nXXn69Onlcrmx7wnPkiVLyl/60pfK5TLdk6yoK4t7woQJGD9+fM32KVOmoL29HQAwadIkvP3223jv\nvffQ19eHgwcP4sILLwQA3HjjjfjhD3+YaZtDo7onQ4cOxR//8R9jyJAhVdsb+Z488sgjuO666zB4\n8GCMHTsWZ599Np577rmGuCeMV155BZdddhkA4IorrsAPfvADAPJ70yhL+Y4cORJvvvkmAKC/vx+j\nR48G0Nj3hFEul/Hggw/iuuuuA0D3JCvqSrht+MEPfoDzzz8fgwcPRm9vLzo6OiqvjR49Gr29vTm2\nLnvEhWAa+Z7s3r276r13dHSgt7e3Zns935Ouri488sgjAI5PKezcuROA+t40AsuXL8eSJUvwwQ9+\nEH/zN3+Du+66C0Bj3xPGz3/+c7S1tWHcuHEA6J5kRdA87hDMnDkTe/bsqdm+bNkyzJkzR3vsli1b\ncPvtt2PDhg2hmpcLae5JvUL3RI3u3nznO9/B5z73OXzpS1/C3Llz8YEPfEB5nnpa/U91T77yla/g\nnnvuwT333IOPfexjeOihh3DTTTcp+5BGuCf8M7RmzRp8/OMf156nnu5JLBROuJOK7q5du/Bnf/Zn\nuP/++9HZ2QnguOW0a9euqn2YG6xI+ByINPI9GT16dMXCBI6/946Ojrq5JwzTvfnP//xPAMC2bdvw\n4x//GID83hT5Hojo7sn111+Pxx9/HAAwf/58LFq0CEBj3xMAOHr0KNauXVsV4Fnv9yQW6tZVXubq\nyvT392P27Nn46le/iksuuaSyfeTIkTj11FPx3HPPoVwu4/7778e8efPyaG4mlCW1dsRtjXxP5s6d\ni+9973t49913sX37drzyyiu48MIL0d7e3jD35I033gAAHDt2DF/+8pdx8803A1Dfm0bg7LPPrlSA\n/OlPf1qJj2jkewIAjz/+OCZOnIhRo0ZVtjX6PcmM/OLi/PMf//Ef5Y6OjvLJJ59cbmtrK1911VXl\ncrlc/tKXvlQ+5ZRTylOmTKn8vPHGG+VyuVz+1a9+Vf7Qhz5UHjduXPmzn/1sns0PguqelMvl8pln\nnlkePnx4ubm5udzR0VHeunVruVxu7Hvyla98pTxu3LjyueeeW4kkLpfr/54wVqxYUR4/fnx5/Pjx\n5S984QtVr6nuTb3zy1/+snzhhReWJ0+eXL744ovLmzZtqrzWqPekXC6X//Iv/7K8cuXKmu2NfE+y\ngkqeEgRBEESBqFtXOUEQBEHUIyTcBEEQBFEgSLgJgiAIokCQcBMEQRBEgSDhJgiCIIgCQcJNEARB\nEAWChJsgCIIgCgQJN0EQBEEUiP8Pui9nt03eM7IAAAAASUVORK5CYII=\n",
"text": [
"<matplotlib.figure.Figure at 0x109543710>"
]
}
],
"prompt_number": 60
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"Features in the works"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* Automatic alignment for math operations\n",
" * \"outer\" join like pandas?\n",
" * \"inner\" join to avoid memory blow-ups?\n",
"* A constructor to make a `DataArray` directly\n",
"* More features from pandas (dropna, idxmax, count, switch to nanmean/nanstd/nanvar, etc.)\n",
"* Incremental writes of data to disk"
]
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"Thoughts for PyData"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* Heterogeneous arrays aren\u2019t really necessary (if you have good enough containers)\n",
"* The pandas.Index is really powerful\n",
"* Out of core computation is essential"
]
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"Thanks to"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Co-workers at [The Climate Corporation](http://climate.com) who have contributed to or tested pre-release versions of xray:\n",
"* Alex Kleeman\n",
"* Holly Dail\n",
"* Todd Small\n",
"* Eugene Brevdo\n",
"\n",
"You!"
]
}
],
"metadata": {}
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment