Permalink
Browse files

initial commit

  • Loading branch information...
0 parents commit 16740dfe88aec2a77bdc13f13a1fa5aae88ddd14 @jcarbaugh jcarbaugh committed Jun 7, 2012
Showing with 370 additions and 0 deletions.
  1. +27 −0 LICENSE
  2. +83 −0 README.rst
  3. +1 −0 census/__init__.py
  4. +167 −0 census/core.py
  5. +65 −0 census/states.py
  6. +1 −0 requirements.txt
  7. +26 −0 setup.py
27 LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2012, Sunlight Labs
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * Neither the name of Sunlight Labs nor the names of its contributors may be
+ used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,83 @@
+======
+census
+======
+
+A simple wrapper for the United States Census Bureau's API.
+
+Provides access to both the ACS and SF1 data sets.
+
+
+Requirements
+============
+
+* python 2.6 or 2.7
+* requests
+
+
+Usage
+=====
+
+First, get yourself a `Census API key <http://www.census.gov/developers/>`_.
+
+::
+
+ from census import Census
+
+ c = Census("MY_API_KEY")
+
+Full geometry specifications are available for `ACS <http://thedataweb.rm.census.gov/data/acs5geo.html>`_ and `SF1 <http://thedataweb.rm.census.gov/data/sf1geo.html>`_
+
+ACS Geometries
+--------------
+
+* state(fields, state_fips)
+* state_county(fields, state_fips, county_fips)
+* state_county_subdivision(fields, state_fips, county_fips, subdiv_fips)
+* state_county_tract(fields, state_fips, county_fips, tract)
+* state_place(fields, state_fips, place)
+* state_district(fields, state_fips, district)
+* us(fields)
+
+
+SF1 Geometries
+--------------
+
+* state(fields, state_fips)
+* state_county(fields, state_fips, county_fips)
+* state_county_subdivision(fields, state_fips, county_fips, subdiv_fips)
+* state_county_tract(fields, state_fips, county_fips, tract)
+* state_place(fields, state_fips, place)
+* state_district(fields, state_fips, district)
+* state_msa(fields, state_fips, msa)
+* state_csa(fields, state_fips, csa)
+* state_district_place(fields, state_fips, district, place)
+* state_zip(fields, state_fips, zip)
+
+
+
+
+
+
+http://www.census.gov/developers/data/sf1.xml
+http://www.census.gov/developers/data/2010acs5_variables.xml
+
+States
+======
+
+The states module makes it easy to convert between state abbreviations and FIPS
+codes. Access attributes by state abbreviation to get the corresponding FIPS
+code::
+
+ >>> from census import states
+ >>> print states.MD
+ 24
+
+Convert FIPS to state abbreviation using the FIPS dict::
+
+ >>> print states.FIPS['24']
+ MD
+
+
+Examples
+========
+
@@ -0,0 +1 @@
+from census.core import Census, ALL
@@ -0,0 +1,167 @@
+import json
+import requests
+from xml.etree.ElementTree import XML
+
+ALL = '*'
+ENDPOINT_URL = 'http://thedataweb.rm.census.gov/data/%s/%s'
+
+
+def list_or_str(v):
+ """ Convert a single value into a list.
+ """
+ if isinstance(v, (list, tuple)):
+ return v
+ return [v]
+
+
+class CensusException(Exception):
+ pass
+
+
+class Client(object):
+
+ def __init__(self, key):
+ self._session = requests.session(params={'key': key})
+ self._key = key
+
+ def fields(self, flat=False):
+
+ data = {}
+
+ resp = requests.get(self.fields_url)
+ doc = XML(resp.text)
+
+ if flat:
+
+ for elem in doc.iter('variable'):
+ data[elem.attrib['name']] = "%s: %s" % (elem.attrib['concept'], elem.text)
+
+ else:
+
+ for concept_elem in doc.iter('concept'):
+
+ concept = concept_elem.attrib['name']
+ variables = {}
+
+ for variable_elem in concept_elem.iter('variable'):
+ variables[variable_elem.attrib['name']] = variable_elem.text
+
+ data[concept] = variables
+
+ return data
+
+ def get(self, fields, geo, year=None):
+
+ if year is None:
+ year = self.default_year
+
+ fields = list_or_str(fields)
+
+ url = ENDPOINT_URL % (year, self.dataset)
+
+ params = {
+ 'get': ",".join(fields),
+ 'for': geo['for'],
+ }
+
+ if 'in' in geo:
+ params['in'] = geo['in']
+
+ resp = self._session.get(url, params=params)
+
+ if resp.status_code == 200:
+
+ data = json.loads(resp.text)
+ # return data
+
+ headers = data[0]
+ return [dict(zip(headers, d)) for d in data[1:]]
+
+ elif resp.status_code == 204:
+ return []
+
+ else:
+ raise CensusException(resp.text)
+
+ def state(self, fields, state_fips, **kwargs):
+ return self.get(fields, geo={
+ 'for': 'state:%s' % state_fips,
+ })
+
+ def state_county(self, fields, state_fips, county_fips):
+ return self.get(fields, geo={
+ 'for': 'county:%s' % county_fips,
+ 'in': 'state:%s' % state_fips,
+ })
+
+ def state_county_subdivision(self, fields, state_fips, county_fips, subdiv_fips):
+ return self.get(fields, geo={
+ 'for': 'county subdivision:%s' % subdiv_fips,
+ 'in': 'state:%s county:%s' % (state_fips, county_fips),
+ })
+
+ def state_county_tract(self, fields, state_fips, county_fips, tract):
+ return self.get(fields, geo={
+ 'for': 'tract:%s' % tract,
+ 'in': 'state:%s county:%s' % (state_fips, county_fips),
+ })
+
+ def state_place(self, fields, state_fips, place):
+ return self.get(fields, geo={
+ 'for': 'place:%s' % place,
+ 'in': 'state:%s' % state_fips,
+ })
+
+ def state_district(self, fields, state_fips, district):
+ return self.get(fields, geo={
+ 'for': 'congressional district:%s' % district,
+ 'in': 'state:%s' % state_fips,
+ })
+
+
+class ACSClient(Client):
+
+ default_year = 2010
+ dataset = 'acs5'
+ fields_url = "http://www.census.gov/developers/data/2010acs5_variables.xml"
+
+ def us(self, fields, **kwargs):
+ return self.get(fields, geo={'for': 'us:1'})
+
+
+class SF1Client(Client):
+
+ default_year = 2010
+ dataset = 'sf1'
+ fields_url = "http://www.census.gov/developers/data/sf1.xml"
+
+ def state_msa(self, fields, state_fips, msa):
+ return self.get(fields, geo={
+ 'for': 'metropolitan statistical area/micropolitan statistical area:%s' % msa,
+ 'in': 'state:%s' % state_fips,
+ })
+
+ def state_csa(self, fields, state_fips, csa):
+ return self.get(fields, geo={
+ 'for': 'combined statistical area:%s' % csa,
+ 'in': 'state:%s' % state_fips,
+ })
+
+ def state_district_place(self, fields, state_fips, district, place):
+ return self.get(fields, geo={
+ 'for': 'place:' % place,
+ 'in': 'state:%s congressional district:%s' % (state_fips, district),
+ })
+
+ def state_zip(self, fields, state_fips, zip):
+ return self.get(fields, geo={
+ 'for': 'zip code tabulation area:%s' % zip,
+ 'in': 'state:%s' % state_fips,
+ })
+
+
+class Census(object):
+
+ def __init__(self, key):
+ self.acs = ACSClient(key)
+ self.sf1 = SF1Client(key)
@@ -0,0 +1,65 @@
+STATES = {
+ 'AK': '02',
+ 'AL': '01',
+ 'AR': '05',
+ 'AS': '60',
+ 'AZ': '04',
+ 'CA': '06',
+ 'CO': '08',
+ 'CT': '09',
+ 'DC': '11',
+ 'DE': '10',
+ 'FL': '12',
+ 'GA': '13',
+ 'GU': '66',
+ 'HI': '15',
+ 'IA': '19',
+ 'ID': '16',
+ 'IL': '17',
+ 'IN': '18',
+ 'KS': '20',
+ 'KY': '21',
+ 'LA': '22',
+ 'MA': '25',
+ 'MD': '24',
+ 'ME': '23',
+ 'MI': '26',
+ 'MN': '27',
+ 'MO': '29',
+ 'MS': '28',
+ 'MT': '30',
+ 'NC': '37',
+ 'ND': '38',
+ 'NE': '31',
+ 'NH': '33',
+ 'NJ': '34',
+ 'NM': '35',
+ 'NV': '32',
+ 'NY': '36',
+ 'OH': '39',
+ 'OK': '40',
+ 'OR': '41',
+ 'PA': '42',
+ 'PR': '72',
+ 'RI': '44',
+ 'SC': '45',
+ 'SD': '46',
+ 'TN': '47',
+ 'TX': '48',
+ 'UT': '49',
+ 'VA': '51',
+ 'VI': '78',
+ 'VT': '50',
+ 'WA': '53',
+ 'WI': '55',
+ 'WV': '54',
+ 'WY': '56',
+}
+
+FIPS = {v: k for (k, v) in STATES.items()}
+
+""" What I really want to be able to do is states.MD to get the FIPS code.
+ This will set each k, v of STATES as a module attribute.
+"""
+for (s, f) in STATES.items():
+ globals()[s] = f
@@ -0,0 +1 @@
+requests
@@ -0,0 +1,26 @@
+from setuptools import setup, find_packages
+
+long_description = open('README.rst').read()
+
+setup(
+ name="census",
+ version='0.1',
+ py_modules=['census'],
+ author="Jeremy Carbaugh",
+ author_email='jcarbaugh@sunlightfoundation.com',
+ license="BSD",
+ url="http://github.com/sunlightlabs/census",
+ long_description=long_description,
+ packages=find_packages(),
+ description="A wrapper for the US Census Bureau's API",
+ platforms=["any"],
+ classifiers=[
+ "Development Status :: 4 - Beta",
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: BSD License",
+ "Natural Language :: English",
+ "Operating System :: OS Independent",
+ "Programming Language :: Python",
+ ],
+ install_requires=['requests'],
+)

0 comments on commit 16740df

Please sign in to comment.