Skip to content

Commit 1c351ff

Browse files
author
David Stephens
committed
Eliminate Panel for multiple symbols, return MultiIndex DataFrame instead
1 parent 305f4e1 commit 1c351ff

File tree

8 files changed

+39
-26
lines changed

8 files changed

+39
-26
lines changed

docs/source/whatsnew/v0.7.0.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ Enhancements
4242

4343
Backwards incompatible API changes
4444
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
45+
- When requesting multiple symbols from a DailyReader (ex: google, yahoo, IEX)
46+
a MultiIndex DataFrame is now returned. Previously Panel or dict of DataFrames
47+
were returned. (:issue:`297`).
48+
4549

4650
.. _whatsnew_070.bug_fixes:
4751

pandas_datareader/base.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
import requests
66

77
import pandas.compat as compat
8-
from pandas import Panel, DataFrame
9-
from pandas import read_csv
8+
from pandas import DataFrame
9+
from pandas import read_csv, concat
1010
from pandas.io.common import urlencode
1111
from pandas.compat import StringIO, bytes_to_str
1212

@@ -239,7 +239,9 @@ def _dl_mult_symbols(self, symbols):
239239
df_na[:] = np.nan
240240
for sym in failed:
241241
stocks[sym] = df_na
242-
return Panel(stocks).swapaxes('items', 'minor')
242+
result = concat(stocks).unstack(level=0)
243+
result.columns.names = ['Attributes', 'Symbols']
244+
return result
243245
except AttributeError:
244246
# cannot construct a panel with just 1D nans indicating no data
245247
msg = "No data fetched using {0!r}"

pandas_datareader/google/daily.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@
99

1010
class GoogleDailyReader(_DailyBaseReader):
1111
"""
12-
Returns DataFrame/Panel of historical stock prices from symbols, over date
13-
range, start to end. To avoid being penalized by Google Finance servers,
14-
pauses between downloading 'chunks' of symbols can be specified.
12+
Returns DataFrame of historical stock prices from
13+
symbols, over date range, start to end. To avoid being penalized by Google
14+
Finance servers, pauses between downloading 'chunks' of symbols can be
15+
specified.
1516
1617
Parameters
1718
----------

pandas_datareader/iex/daily.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@
1616
class IEXDailyReader(_DailyBaseReader):
1717

1818
"""
19-
Returns DataFrame/Panel of historical stock prices from symbols, over date
20-
range, start to end. To avoid being penalized by Google Finance servers,
21-
pauses between downloading 'chunks' of symbols can be specified.
19+
Returns DataFrame of historical stock prices
20+
from symbols, over date range, start to end. To avoid being penalized by
21+
IEX servers, pauses between downloading 'chunks' of symbols can be
22+
specified.
2223
2324
Parameters
2425
----------
@@ -110,5 +111,7 @@ def _read_lines(self, out):
110111
df = df.loc[sstart:send]
111112
result.update({symbol: df})
112113
if len(result) > 1:
114+
result = pd.concat(result).unstack(level=0)
115+
result.columns.names = ['Attributes', 'Symbols']
113116
return result
114117
return result[self.symbols]

pandas_datareader/stooq.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
class StooqDailyReader(_DailyBaseReader):
55

66
"""
7-
Returns DataFrame/Panel of historical stock prices from symbols, over date
8-
range, start to end.
7+
Returns DataFrame/dict of Dataframes of historical stock prices from
8+
symbols, over date range, start to end.
99
1010
Parameters
1111
----------
@@ -18,7 +18,7 @@ class StooqDailyReader(_DailyBaseReader):
1818
Time, in seconds, to pause between consecutive queries of chunks. If
1919
single value given for symbol, represents the pause between retries.
2020
chunksize : int, default 25
21-
Number of symbols to download consecutively before intiating pause.
21+
Number of symbols to download consecutively before initiating pause.
2222
session : Session, default None
2323
requests.sessions.Session instance to be used
2424

pandas_datareader/tests/google/test_google.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,8 @@ def test_get_multi1(self):
126126
@skip_on_exception(RemoteDataError)
127127
def test_get_multi_invalid(self):
128128
sl = ['AAPL', 'AMZN', 'INVALID']
129-
pan = web.get_data_google(sl, '2012')
130-
assert 'INVALID' in pan.minor_axis
129+
data = web.get_data_google(sl, '2012')
130+
assert 'INVALID' in data.columns.levels[1]
131131

132132
def test_get_multi_all_invalid(self):
133133
sl = ['INVALID', 'INVALID2', 'INVALID3']

pandas_datareader/tests/test_iex_daily.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from datetime import datetime
22

3+
from pandas import DataFrame, MultiIndex
4+
35
import pytest
46

57
import pandas_datareader.data as web
@@ -43,21 +45,22 @@ def test_single_symbol(self):
4345
def test_multiple_symbols(self):
4446
syms = ["AAPL", "MSFT", "TSLA"]
4547
df = web.DataReader(syms, "iex", self.start, self.end)
46-
assert sorted(list(df)) == syms
48+
assert sorted(list(df.columns.levels[1])) == syms
4749
for sym in syms:
48-
assert len(df[sym] == 578)
50+
assert len(df.xs(sym, level='Symbols', axis=1) == 578)
4951

5052
def test_multiple_symbols_2(self):
5153
syms = ["AAPL", "MSFT", "TSLA"]
5254
good_start = datetime(2017, 2, 9)
5355
good_end = datetime(2017, 5, 24)
5456
df = web.DataReader(syms, "iex", good_start, good_end)
55-
assert isinstance(df, dict)
56-
assert len(df) == 3
57-
assert sorted(list(df)) == syms
57+
assert isinstance(df, DataFrame)
58+
assert isinstance(df.columns, MultiIndex)
59+
assert len(df.columns.levels[1]) == 3
60+
assert sorted(list(df.columns.levels[1])) == syms
5861

59-
a = df["AAPL"]
60-
t = df["TSLA"]
62+
a = df.xs("AAPL", axis=1, level='Symbols')
63+
t = df.xs("TSLA", axis=1, level='Symbols')
6164

6265
assert len(a) == 73
6366
assert len(t) == 73

pandas_datareader/tests/yahoo/test_yahoo.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,9 @@ def test_get_data_null_as_missing_data(self, adj_pr):
171171

172172
@skip_on_exception(RemoteDataError)
173173
def test_get_data_multiple_symbols_two_dates(self):
174-
pan = web.get_data_yahoo(['GE', 'MSFT', 'INTC'], 'JAN-01-12',
175-
'JAN-31-12')
176-
result = pan.Close.loc['01-18-12'].T
174+
data = web.get_data_yahoo(['GE', 'MSFT', 'INTC'], 'JAN-01-12',
175+
'JAN-31-12')
176+
result = data.Close.loc['01-18-12'].T
177177
assert result.size == 3
178178

179179
# sanity checking
@@ -183,7 +183,7 @@ def test_get_data_multiple_symbols_two_dates(self):
183183
[18.58, 28.31, 25.13],
184184
[19.03, 28.16, 25.52],
185185
[18.81, 28.82, 25.87]])
186-
df = pan.Open
186+
df = data.Open
187187
result = df[(df.index >= 'Jan-15-12') & (df.index <= 'Jan-20-12')]
188188
assert expected.shape == result.shape
189189

@@ -279,4 +279,4 @@ def test_yahoo_DataReader_multi(self):
279279
start = datetime(2010, 1, 1)
280280
end = datetime(2015, 5, 9)
281281
result = web.DataReader(['AAPL', 'F'], 'yahoo-actions', start, end)
282-
assert isinstance(result, pd.Panel)
282+
assert isinstance(result, dict)

0 commit comments

Comments
 (0)