diff --git a/doc/source/remote_data.rst b/doc/source/remote_data.rst index 33db9de48d9e7..bba3db86d837c 100644 --- a/doc/source/remote_data.rst +++ b/doc/source/remote_data.rst @@ -143,6 +143,12 @@ World Bank `World Bank's World Development Indicators `__ by using the ``wb`` I/O functions. +Indicators +~~~~~~~~~~ + +Either from exploring the World Bank site, or using the search function included, +every world bank indicator is accessible. + For example, if you wanted to compare the Gross Domestic Products per capita in constant dollars in North America, you would use the ``search`` function: @@ -254,3 +260,56 @@ populations in rich countries tend to use cellphones at a higher rate: Skew: -2.314 Prob(JB): 1.35e-26 Kurtosis: 11.077 Cond. No. 45.8 ============================================================================== + +Country Codes +~~~~~~~~~~~~~ + +.. versionadded:: 0.15.1 + +The ``country`` argument accepts a string or list of mixed +`two `__ or `three `__ character +ISO country codes, as well as dynamic `World Bank exceptions `__ to the ISO standards. + +For a list of the the hard-coded country codes (used solely for error handling logic) see ``pandas.io.wb.country_codes``. + +Problematic Country Codes & Indicators +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. note:: + + The World Bank's country list and indicators are dynamic. As of 0.15.1, + :func:`wb.download()` is more flexible. To achieve this, the warning + and exception logic changed. + +The world bank converts some country codes, +in their response, which makes error checking by pandas difficult. +Retired indicators still persist in the search. + +Given the new flexibility of 0.15.1, improved error handling by the user +may be necessary for fringe cases. + +To help identify issues: + +There are at least 4 kinds of country codes: + +1. Standard (2/3 digit ISO) - returns data, will warn and error properly. +2. Non-standard (WB Exceptions) - returns data, but will falsely warn. +3. Blank - silently missing from the response. +4. Bad - causes the entire response from WB to fail, always exception inducing. + +There are at least 3 kinds of indicators: + +1. Current - Returns data. +2. Retired - Appears in search results, yet won't return data. +3. Bad - Will not return data. + +Use the ``errors`` argument to control warnings and exceptions. Setting +errors to ignore or warn, won't stop failed responses. (ie, 100% bad +indicators, or a single "bad" (#4 above) country code). + +See docstrings for more info. + + + + + diff --git a/doc/source/whatsnew/v0.15.1.txt b/doc/source/whatsnew/v0.15.1.txt index 7b3aaa00e7ea9..4901c68f8fd23 100644 --- a/doc/source/whatsnew/v0.15.1.txt +++ b/doc/source/whatsnew/v0.15.1.txt @@ -19,18 +19,17 @@ users upgrade to this version. API changes ~~~~~~~~~~~ - - + .. _whatsnew_0151.enhancements: Enhancements ~~~~~~~~~~~~ - Added option to select columns when importing Stata files (:issue:`7935`) - - Qualify memory usage in ``DataFrame.info()`` by adding ``+`` if it is a lower bound (:issue:`8578`) - - +- Added support for 3-character ISO and non-standard country codes in :func:``io.wb.download()`` (:issue:`8482`) +- :ref:`World Bank data requests ` now raise Warnings and ValueErrors based on an ``errors`` argument, as well as a list of hard-coded country codes and the World Bank's JSON response. In prior versions, the error messages didn't look at the World Bank's JSON response. Problem-inducing input were simply dropped prior to the request. The issue was that many good countries were cropped in the hard-coded approach. All countries will work now, but some bad countries will raise exceptions because some edge cases break the entire response. + .. _whatsnew_0151.performance: Performance @@ -41,8 +40,7 @@ Performance Experimental ~~~~~~~~~~~~ - - + .. _whatsnew_0151.bug_fixes: Bug Fixes diff --git a/pandas/io/tests/test_wb.py b/pandas/io/tests/test_wb.py index 7c86f582dae1a..f0caf263ffd87 100644 --- a/pandas/io/tests/test_wb.py +++ b/pandas/io/tests/test_wb.py @@ -14,19 +14,15 @@ class TestWB(tm.TestCase): @slow @network def test_wdi_search(self): - raise nose.SkipTest - - expected = {u('id'): {2634: u('GDPPCKD'), - 4649: u('NY.GDP.PCAP.KD'), - 4651: u('NY.GDP.PCAP.KN'), - 4653: u('NY.GDP.PCAP.PP.KD')}, - u('name'): {2634: u('GDP per Capita, constant US$, ' - 'millions'), - 4649: u('GDP per capita (constant 2000 US$)'), - 4651: u('GDP per capita (constant LCU)'), - 4653: u('GDP per capita, PPP (constant 2005 ' + + expected = {u('id'): {6716: u('NY.GDP.PCAP.KD'), + 6718: u('NY.GDP.PCAP.KN'), + 6720: u('NY.GDP.PCAP.PP.KD')}, + u('name'): {6716: u('GDP per capita (constant 2005 US$)'), + 6718: u('GDP per capita (constant LCU)'), + 6720: u('GDP per capita, PPP (constant 2011 ' 'international $)')}} - result = search('gdp.*capita.*constant').ix[:, :2] + result = search('gdp.*capita.*constant').loc[6716:,['id','name']] expected = pandas.DataFrame(expected) expected.index = result.index assert_frame_equal(result, expected) @@ -34,22 +30,93 @@ def test_wdi_search(self): @slow @network def test_wdi_download(self): - raise nose.SkipTest - expected = {'GDPPCKN': {(u('United States'), u('2003')): u('40800.0735367688'), (u('Canada'), u('2004')): u('37857.1261134552'), (u('United States'), u('2005')): u('42714.8594790102'), (u('Canada'), u('2003')): u('37081.4575704003'), (u('United States'), u('2004')): u('41826.1728310667'), (u('Mexico'), u('2003')): u('72720.0691255285'), (u('Mexico'), u('2004')): u('74751.6003347038'), (u('Mexico'), u('2005')): u('76200.2154469437'), (u('Canada'), u('2005')): u('38617.4563629611')}, 'GDPPCKD': {(u('United States'), u('2003')): u('40800.0735367688'), (u('Canada'), u('2004')): u('34397.055116118'), (u('United States'), u('2005')): u('42714.8594790102'), (u('Canada'), u('2003')): u('33692.2812368928'), (u('United States'), u('2004')): u('41826.1728310667'), (u('Mexico'), u('2003')): u('7608.43848670658'), (u('Mexico'), u('2004')): u('7820.99026814334'), (u('Mexico'), u('2005')): u('7972.55364129367'), (u('Canada'), u('2005')): u('35087.8925933298')}} + # Test a bad indicator with double (US), triple (USA), + # standard (CA, MX), non standard (KSV), + # duplicated (US, US, USA), and unknown (BLA) country codes + + # ...but NOT a crash inducing country code (World bank strips pandas + # users of the luxury of laziness, because they create their + # own exceptions, and don't clean up legacy country codes. + # ...but NOT a retired indicator (User should want it to error.) + + cntry_codes = ['CA', 'MX', 'USA', 'US', 'US', 'KSV', 'BLA'] + inds = ['NY.GDP.PCAP.CD','BAD.INDICATOR'] + + expected = {'NY.GDP.PCAP.CD': {('Canada', '2003'): 28026.006013044702, ('Mexico', '2003'): 6601.0420648056606, ('Canada', '2004'): 31829.522562759001, ('Kosovo', '2003'): 1969.56271307405, ('Mexico', '2004'): 7042.0247834044303, ('United States', '2004'): 41928.886136479705, ('United States', '2003'): 39682.472247320402, ('Kosovo', '2004'): 2135.3328465238301}} expected = pandas.DataFrame(expected) - result = download(country=['CA', 'MX', 'US', 'junk'], indicator=['GDPPCKD', - 'GDPPCKN', 'junk'], start=2003, end=2005) + expected.sort(inplace=True) + result = download(country=cntry_codes, indicator=inds, + start=2003, end=2004, errors='ignore') + result.sort(inplace=True) expected.index = result.index assert_frame_equal(result, pandas.DataFrame(expected)) + @slow + @network + def test_wdi_download_w_retired_indicator(self): + + cntry_codes = ['CA', 'MX', 'US'] + # Despite showing up in the search feature, and being listed online, + # the api calls to GDPPCKD don't work in their own query builder, nor + # pandas module. GDPPCKD used to be a common symbol. + # This test is written to ensure that error messages to pandas users + # continue to make sense, rather than a user getting some missing + # key error, cause their JSON message format changed. If + # World bank ever finishes the deprecation of this symbol, + # this nose test should still pass. + + inds = ['GDPPCKD'] + + try: + result = download(country=cntry_codes, indicator=inds, + start=2003, end=2004, errors='ignore') + # If for some reason result actually ever has data, it's cause WB + # fixed the issue with this ticker. Find another bad one. + except ValueError as e: + error_raised = True + error_msg = e.args[0] + + self.assertTrue(error_raised) + self.assertTrue("No indicators returned data." in error_msg) + + # if it ever gets here, it means WB unretired the indicator. + # even if they dropped it completely, it would still get caught above + # or the WB API changed somehow in a really unexpected way. + if len(result) > 0: + raise nose.SkipTest + + + + @slow + @network + def test_wdi_download_w_crash_inducing_countrycode(self): + + cntry_codes = ['CA', 'MX', 'US', 'XXX'] + inds = ['NY.GDP.PCAP.CD'] + + try: + result = download(country=cntry_codes, indicator=inds, + start=2003, end=2004, errors='ignore') + except ValueError as e: + error_raised = True + error_msg = e.args[0] + + self.assertTrue(error_raised) + self.assertTrue("No indicators returned data." in error_msg) + + # if it ever gets here, it means the country code XXX got used by WB + # or the WB API changed somehow in a really unexpected way. + if len(result) > 0: + raise nose.SkipTest + @slow @network def test_wdi_get_countries(self): result = get_countries() self.assertTrue('Zimbabwe' in list(result['name'])) - + self.assertTrue(len(result) > 100) if __name__ == '__main__': nose.runmodule(argv=[__file__, '-vvs', '-x', '--pdb', '--pdb-failure'], - exit=False) + exit=False) \ No newline at end of file diff --git a/pandas/io/wb.py b/pandas/io/wb.py index d815bb19ec8b8..7a9443c4b9ac6 100644 --- a/pandas/io/wb.py +++ b/pandas/io/wb.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + from __future__ import print_function from pandas.compat import map, reduce, range, lrange @@ -5,10 +7,74 @@ from pandas.io import json import pandas import numpy as np +import warnings + +# This list of country codes was pulled from wikipedia during October 2014. +# While some exceptions do exist, it is the best proxy for countries supported +# by World Bank. It is an aggregation of the 2-digit ISO 3166-1 alpha-2, and +# 3-digit ISO 3166-1 alpha-3, codes, with 'all', 'ALL', and 'All' appended ot +# the end. +country_codes = ['AD', 'AE', 'AF', 'AG', 'AI', 'AL', 'AM', 'AO', 'AQ', 'AR', \ + 'AS', 'AT', 'AU', 'AW', 'AX', 'AZ', 'BA', 'BB', 'BD', 'BE', \ + 'BF', 'BG', 'BH', 'BI', 'BJ', 'BL', 'BM', 'BN', 'BO', 'BQ', \ + 'BR', 'BS', 'BT', 'BV', 'BW', 'BY', 'BZ', 'CA', 'CC', 'CD', \ + 'CF', 'CG', 'CH', 'CI', 'CK', 'CL', 'CM', 'CN', 'CO', 'CR', \ + 'CU', 'CV', 'CW', 'CX', 'CY', 'CZ', 'DE', 'DJ', 'DK', 'DM', \ + 'DO', 'DZ', 'EC', 'EE', 'EG', 'EH', 'ER', 'ES', 'ET', 'FI', \ + 'FJ', 'FK', 'FM', 'FO', 'FR', 'GA', 'GB', 'GD', 'GE', 'GF', \ + 'GG', 'GH', 'GI', 'GL', 'GM', 'GN', 'GP', 'GQ', 'GR', 'GS', \ + 'GT', 'GU', 'GW', 'GY', 'HK', 'HM', 'HN', 'HR', 'HT', 'HU', \ + 'ID', 'IE', 'IL', 'IM', 'IN', 'IO', 'IQ', 'IR', 'IS', 'IT', \ + 'JE', 'JM', 'JO', 'JP', 'KE', 'KG', 'KH', 'KI', 'KM', 'KN', \ + 'KP', 'KR', 'KW', 'KY', 'KZ', 'LA', 'LB', 'LC', 'LI', 'LK', \ + 'LR', 'LS', 'LT', 'LU', 'LV', 'LY', 'MA', 'MC', 'MD', 'ME', \ + 'MF', 'MG', 'MH', 'MK', 'ML', 'MM', 'MN', 'MO', 'MP', 'MQ', \ + 'MR', 'MS', 'MT', 'MU', 'MV', 'MW', 'MX', 'MY', 'MZ', 'NA', \ + 'NC', 'NE', 'NF', 'NG', 'NI', 'NL', 'NO', 'NP', 'NR', 'NU', \ + 'NZ', 'OM', 'PA', 'PE', 'PF', 'PG', 'PH', 'PK', 'PL', 'PM', \ + 'PN', 'PR', 'PS', 'PT', 'PW', 'PY', 'QA', 'RE', 'RO', 'RS', \ + 'RU', 'RW', 'SA', 'SB', 'SC', 'SD', 'SE', 'SG', 'SH', 'SI', \ + 'SJ', 'SK', 'SL', 'SM', 'SN', 'SO', 'SR', 'SS', 'ST', 'SV', \ + 'SX', 'SY', 'SZ', 'TC', 'TD', 'TF', 'TG', 'TH', 'TJ', 'TK', \ + 'TL', 'TM', 'TN', 'TO', 'TR', 'TT', 'TV', 'TW', 'TZ', 'UA', \ + 'UG', 'UM', 'US', 'UY', 'UZ', 'VA', 'VC', 'VE', 'VG', 'VI', \ + 'VN', 'VU', 'WF', 'WS', 'YE', 'YT', 'ZA', 'ZM', 'ZW', \ + 'ABW', 'AFG', 'AGO', 'AIA', 'ALA', 'ALB', 'AND', 'ARE', \ + 'ARG', 'ARM', 'ASM', 'ATA', 'ATF', 'ATG', 'AUS', 'AUT', \ + 'AZE', 'BDI', 'BEL', 'BEN', 'BES', 'BFA', 'BGD', 'BGR', \ + 'BHR', 'BHS', 'BIH', 'BLM', 'BLR', 'BLZ', 'BMU', 'BOL', \ + 'BRA', 'BRB', 'BRN', 'BTN', 'BVT', 'BWA', 'CAF', 'CAN', \ + 'CCK', 'CHE', 'CHL', 'CHN', 'CIV', 'CMR', 'COD', 'COG', \ + 'COK', 'COL', 'COM', 'CPV', 'CRI', 'CUB', 'CUW', 'CXR', \ + 'CYM', 'CYP', 'CZE', 'DEU', 'DJI', 'DMA', 'DNK', 'DOM', \ + 'DZA', 'ECU', 'EGY', 'ERI', 'ESH', 'ESP', 'EST', 'ETH', \ + 'FIN', 'FJI', 'FLK', 'FRA', 'FRO', 'FSM', 'GAB', 'GBR', \ + 'GEO', 'GGY', 'GHA', 'GIB', 'GIN', 'GLP', 'GMB', 'GNB', \ + 'GNQ', 'GRC', 'GRD', 'GRL', 'GTM', 'GUF', 'GUM', 'GUY', \ + 'HKG', 'HMD', 'HND', 'HRV', 'HTI', 'HUN', 'IDN', 'IMN', \ + 'IND', 'IOT', 'IRL', 'IRN', 'IRQ', 'ISL', 'ISR', 'ITA', \ + 'JAM', 'JEY', 'JOR', 'JPN', 'KAZ', 'KEN', 'KGZ', 'KHM', \ + 'KIR', 'KNA', 'KOR', 'KWT', 'LAO', 'LBN', 'LBR', 'LBY', \ + 'LCA', 'LIE', 'LKA', 'LSO', 'LTU', 'LUX', 'LVA', 'MAC', \ + 'MAF', 'MAR', 'MCO', 'MDA', 'MDG', 'MDV', 'MEX', 'MHL', \ + 'MKD', 'MLI', 'MLT', 'MMR', 'MNE', 'MNG', 'MNP', 'MOZ', \ + 'MRT', 'MSR', 'MTQ', 'MUS', 'MWI', 'MYS', 'MYT', 'NAM', \ + 'NCL', 'NER', 'NFK', 'NGA', 'NIC', 'NIU', 'NLD', 'NOR', \ + 'NPL', 'NRU', 'NZL', 'OMN', 'PAK', 'PAN', 'PCN', 'PER', \ + 'PHL', 'PLW', 'PNG', 'POL', 'PRI', 'PRK', 'PRT', 'PRY', \ + 'PSE', 'PYF', 'QAT', 'REU', 'ROU', 'RUS', 'RWA', 'SAU', \ + 'SDN', 'SEN', 'SGP', 'SGS', 'SHN', 'SJM', 'SLB', 'SLE', \ + 'SLV', 'SMR', 'SOM', 'SPM', 'SRB', 'SSD', 'STP', 'SUR', \ + 'SVK', 'SVN', 'SWE', 'SWZ', 'SXM', 'SYC', 'SYR', 'TCA', \ + 'TCD', 'TGO', 'THA', 'TJK', 'TKL', 'TKM', 'TLS', 'TON', \ + 'TTO', 'TUN', 'TUR', 'TUV', 'TWN', 'TZA', 'UGA', 'UKR', \ + 'UMI', 'URY', 'USA', 'UZB', 'VAT', 'VCT', 'VEN', 'VGB', \ + 'VIR', 'VNM', 'VUT', 'WLF', 'WSM', 'YEM', 'ZAF', 'ZMB', \ + 'ZWE', 'all', 'ALL', 'All'] -def download(country=['MX', 'CA', 'US'], indicator=['GDPPCKD', 'GDPPCKN'], - start=2003, end=2005): +def download(country=['MX', 'CA', 'US'], indicator=['NY.GDP.MKTP.CD', 'NY.GNS.ICTR.ZS'], + start=2003, end=2005,errors='warn'): """ Download data series from the World Bank's World Development Indicators @@ -17,91 +83,133 @@ def download(country=['MX', 'CA', 'US'], indicator=['GDPPCKD', 'GDPPCKN'], indicator: string or list of strings taken from the ``id`` field in ``WDIsearch()`` + country: string or list of strings. ``all`` downloads data for all countries - ISO-2 character codes select individual countries (e.g.``US``,``CA``) + 2 or 3 character ISO country codes select individual + countries (e.g.``US``,``CA``) or (e.g.``USA``,``CAN``). The codes + can be mixed. + + The two ISO lists of countries, provided by wikipedia, are hardcoded + into pandas as of 11/10/2014. + start: int First year of the data series + end: int Last year of the data series (inclusive) - + + errors: str {'ignore', 'warn', 'raise'}, default 'warn' + Country codes are validated against a hardcoded list. This controls + the outcome of that validation, and attempts to also apply + to the results from world bank. + + errors='raise', will raise a ValueError on a bad country code. + Returns ------- - ``pandas`` DataFrame with columns: country, iso2c, year, indicator value. + ``pandas`` DataFrame with columns: country, iso_code, year, + indicator value. + """ - # Are ISO-2 country codes valid? - valid_countries = [ - "AG", "AL", "AM", "AO", "AR", "AT", "AU", "AZ", "BB", "BD", "BE", "BF", - "BG", "BH", "BI", "BJ", "BO", "BR", "BS", "BW", "BY", "BZ", "CA", "CD", - "CF", "CG", "CH", "CI", "CL", "CM", "CN", "CO", "CR", "CV", "CY", "CZ", - "DE", "DK", "DM", "DO", "DZ", "EC", "EE", "EG", "ER", "ES", "ET", "FI", - "FJ", "FR", "GA", "GB", "GE", "GH", "GM", "GN", "GQ", "GR", "GT", "GW", - "GY", "HK", "HN", "HR", "HT", "HU", "ID", "IE", "IL", "IN", "IR", "IS", - "IT", "JM", "JO", "JP", "KE", "KG", "KH", "KM", "KR", "KW", "KZ", "LA", - "LB", "LC", "LK", "LS", "LT", "LU", "LV", "MA", "MD", "MG", "MK", "ML", - "MN", "MR", "MU", "MW", "MX", "MY", "MZ", "NA", "NE", "NG", "NI", "NL", - "NO", "NP", "NZ", "OM", "PA", "PE", "PG", "PH", "PK", "PL", "PT", "PY", - "RO", "RU", "RW", "SA", "SB", "SC", "SD", "SE", "SG", "SI", "SK", "SL", - "SN", "SR", "SV", "SY", "SZ", "TD", "TG", "TH", "TN", "TR", "TT", "TW", - "TZ", "UA", "UG", "US", "UY", "UZ", "VC", "VE", "VN", "VU", "YE", "ZA", - "ZM", "ZW", "all" - ] if type(country) == str: country = [country] - bad_countries = np.setdiff1d(country, valid_countries) - country = np.intersect1d(country, valid_countries) - country = ';'.join(country) + + bad_countries = np.setdiff1d(country, country_codes) + + # Validate the input + if len(bad_countries) > 0: + tmp = ", ".join(bad_countries) + if errors == 'raise': + raise ValueError("Invalid Country Code(s): %s" % tmp) + if errors == 'warn': + warnings.warn('Non-standard ISO country codes: %s' % tmp) + # Work with a list of indicators if type(indicator) == str: indicator = [indicator] + # Download data = [] - bad_indicators = [] + bad_indicators = {} for ind in indicator: - try: - tmp = _get_data(ind, country, start, end) - tmp.columns = ['country', 'iso2c', 'year', ind] - data.append(tmp) - except: - bad_indicators.append(ind) - # Warn - if len(bad_indicators) > 0: - print('Failed to obtain indicator(s): %s' % '; '.join(bad_indicators)) - print('The data may still be available for download at ' - 'http://data.worldbank.org') - if len(bad_countries) > 0: - print('Invalid ISO-2 codes: %s' % ' '.join(bad_countries)) - # Merge WDI series + one_indicator_data,msg = _get_data(ind, country, start, end) + if msg == "Success": + data.append(one_indicator_data) + else: + bad_indicators[ind] = msg + + if len(bad_indicators.keys()) > 0: + bad_ind_msgs = [i + " : " + m for i,m in bad_indicators.items()] + bad_ind_msgs = "\n\n".join(bad_ind_msgs) + bad_ind_msgs = "\n\nInvalid Indicators:\n\n%s" % bad_ind_msgs + if errors == 'raise': + raise ValueError(bad_ind_msgs) + if errors == 'warn': + warnings.warn(bad_ind_msgs) + + # Confirm we actually got some data, and build Dataframe if len(data) > 0: out = reduce(lambda x, y: x.merge(y, how='outer'), data) - # Clean - out = out.drop('iso2c', axis=1) + out = out.drop('iso_code', axis=1) out = out.set_index(['country', 'year']) out = out.convert_objects(convert_numeric=True) return out - + else: + msg = "No indicators returned data." + if errors == 'ignore': + msg += " Set errors='warn' for more information." + raise ValueError(msg) def _get_data(indicator="NY.GNS.ICTR.GN.ZS", country='US', start=2002, end=2005): + + if type(country) == str: + country = [country] + + countries = ';'.join(country) + # Build URL for api call - url = ("http://api.worldbank.org/countries/" + country + "/indicators/" + + url = ("http://api.worldbank.org/countries/" + countries + "/indicators/" + indicator + "?date=" + str(start) + ":" + str(end) + "&per_page=25000&format=json") + # Download with urlopen(url) as response: data = response.read() + + # Check to see if there is a possible problem + possible_message = json.loads(data)[0] + if 'message' in possible_message.keys(): + msg = possible_message['message'][0] + try: + msg = msg['key'].split() + ["\n "] + msg['value'].split() + wb_err = ' '.join(msg) + except: + wb_err = "" + if 'key' in msg.keys(): + wb_err = msg['key'] + "\n " + if 'value' in msg.keys(): + wb_err += msg['value'] + error_msg = "Problem with a World Bank Query \n %s" + return None, error_msg % wb_err + + if 'total' in possible_message.keys(): + if possible_message['total'] == 0: + return None, "No results from world bank." + # Parse JSON file data = json.loads(data)[1] country = [x['country']['value'] for x in data] - iso2c = [x['country']['id'] for x in data] + iso_code = [x['country']['id'] for x in data] year = [x['date'] for x in data] value = [x['value'] for x in data] # Prepare output - out = pandas.DataFrame([country, iso2c, year, value]).T - return out - + out = pandas.DataFrame([country, iso_code, year, value]).T + out.columns = ['country', 'iso_code', 'year', indicator] + return out,"Success" def get_countries(): '''Query information about countries @@ -118,7 +226,6 @@ def get_countries(): data = data.rename(columns={'id': 'iso3c', 'iso2Code': 'iso2c'}) return data - def get_indicators(): '''Download information about all World Bank data series ''' @@ -146,7 +253,6 @@ def get_value(x): data.index = pandas.Index(lrange(data.shape[0])) return data - _cached_series = None @@ -190,3 +296,4 @@ def search(string='gdp.*capi', field='name', case=False): idx = data.str.contains(string, case=case) out = _cached_series.ix[idx].dropna() return out +