-->

Etiquetas

Como acceder con SQL a la base de datos del SDSS desde Python

En esta entrada vamos a ver un método muy sencillo de ejecutar sentencias SQL sobre el servidor del Sloan Digital Sky Survey (SDSS). De esta manera podremos obtener una gran cantidad de datos acerca de todo tipo de objetos celestes.

Este post está escrito íntegramente utilizando el Notebook de IPython. También se utilizará el módulo mechanize de Python para navegar por la web y ejecutar de forma interactiva formularios HTML. Iré explicando en detalle cada uno de los pasos, y al final haré un resumen para evitar que nos perdamos en los detalles y mostrar la facilidad de utilización del método utilizado.

Importaciones y referencias

Para una introducción a la ejecución de peticiones SQL sobre el SDSS no se me ocurre mejor recurso que su propio tutorial, junto con su complemento de ejemplos: Sample SQL Queries

Para ver que tablas y vistas existen en la base de datos, y qué información hay disponible en cada una de ellas, una herramienta imprescindible será: Schema Browser

Y, para ejecutar de forma interactiva un SQL contra la base de datos del SDSS, accedase a la página: SQL Search

In [1]:
%matplotlib inline

from __future__ import division

import numpy as np
import pandas as pd
import mechanize
from StringIO import StringIO # Para leer un string como si fuera un fichero

# Generar un cuadro con versiones de las librerías utilizadas en este notebook
#https://github.com/jrjohansson/version_information
%load_ext version_information
%version_information numpy, pandas, StringIO
Out[1]:
SoftwareVersion
Python2.7.9 64bit [GCC 4.4.7 20120313 (Red Hat 4.4.7-1)]
IPython2.3.1
OSLinux 3.13.0 44 generic x86_64 with debian jessie sid
numpy1.9.1
pandas0.15.2
StringIOStringIO
Fri Jan 23 12:06:07 2015 CET
In [2]:
# URL de la página de búsquedas de la base de datos DR10
url = "http://skyserver.sdss3.org/dr10/en/tools/search/sql.aspx"

Preparación del SQL

Antes de comenzar a interactuar desde Python con la página web del SDSS que nos permite enviar una sentencia SQL a su base de datos, vamos a preparar un SQL de prueba en una variable de tipo string. En este SQL buscaremos 10 objetos (¡se trata de una prueba!) de tipo 6 = 'STAR', con fotometría límpia y color azul, en una región del cielo determinada. Eso si, debemos omitir las líneas con comentarios en el SQL (aquellas precedidas de doble guión --) al crear el fichero.

Nota: En una próxima entrada en este blog, se verá cuales son las tablas (o vistas) y campos más útiles para construir nuestras SELECT.

In [3]:
# Como el string a generar ocupa más de una línea
#Utilizamos paréntesis para concatenar automáticamente las líneas

s = ('SELECT TOP 10 '
    'objID, ra, dec, modelMag_u, modelMag_g '
'FROM '
    'PhotoPrimary '
'WHERE '
    'ra BETWEEN 140 and 141 '
    'AND dec BETWEEN 20 and 21 '
    'AND type = 6 '
    'AND clean = 1 '
    'AND modelMag_u - modelMag_g < 0.5')
In [4]:
# Comprobación:
s
Out[4]:
'SELECT TOP 10 objID, ra, dec, modelMag_u, modelMag_g FROM PhotoPrimary WHERE ra BETWEEN 140 and 141 AND dec BETWEEN 20 and 21 AND type = 6 AND clean = 1 AND modelMag_u - modelMag_g < 0.5'

El primer paso para utilizar mechanize será crear un objeto tipo Browser para poder hacer la navegación utilizando sus métodos

In [5]:
br = mechanize.Browser()

A continuación, debemos abrir una sesión con el url que apunta a la página web del SDSS que nos permite hacer las búsquedas SQL:

In [6]:
resp = br.open(url)
In [7]:
resp.info()
Out[7]:
<httplib.HTTPMessage instance at 0x7f3d3bc3d2d8>

Cuando se desea interactuar con una página web, estaremos interesados en conocer los formularios HTML que contiene. Un formulario HTML es una sección del documento comprendida entre las etiquetas:

<FORM> y </FORM>

Un formulario contiene una serie de objetos especiales denominados controles, tales como checkboxes, radio buttons, menus, etc. y labels de estos objetos. El usuario interactua con la página modificando un control, por ejemplo seleccionando una opción, introduciendo un texto en un campo, etc. y haciendo un envío de dicho form al servidor.

Cada form en la página tiene un nombre, si bien este puede en algunos casos estar vacío. Para listar estos nombres de los formularios existentes en la página podemos escribir:

In [8]:
for f in br.forms():
    print f.name
sql

Es decir, en este caso hay un único form en la página, de nombre "sql"

A su vez, cada formulario posee una lista de controles, que tambien poseen un nombre, el cual también puede estar en blanco. Para listar los formularios en la página, junto con sus controles y tipo de cada control, podemos hacer lo siguiente:

In [9]:
for f in br.forms():
    print f.name
    for c in f.controls:
        print '\t',c.name, '\t', c.type
sql
 clear  button
 cmd  textarea
 None  submit
 syntax  checkbox
 format  radio
 reset  reset

Centraremos nuestra atención en el control "cmd" que es un control de texto en el cual debemos escribir nuestro SQL, y el control "format" que, tal como se puede ver en la página web en el navegador, permite controlar el tipo de salida deseada: HTML, XML, CSV, etc. Para poder acceder a estos controles debemos seleccionar previamente el formulario al cual pertenecen:

In [10]:
br.select_form(name="sql")

A continuación, modificaremos el control 'cmd' para introducir nuestro SQL, y el control 'format', para seleccionar la salida en formato csv.

In [11]:
br['cmd'] = s    # El string con el sql
br['format']=['csv'] # formato de salida de los datos
response = br.submit()

El contenido del fichero csv con la respuesta del sql podemos obtenerlo como un string con el método get_data()

In [12]:
print response.get_data()
#Table1
objID,ra,dec,modelMag_u,modelMag_g
1237667293189833237,140.000264887516,20.3528168302492,23.27699,23.07312
1237667293189833455,140.001621936922,20.4298007848266,23.79515,24.28631
1237667293189832757,140.003651871714,20.2546305174986,24.7036,24.82169
1237667430093553674,140.004909940259,20.1719995965921,24.04017,24.81654
1237667430093488938,140.009035667549,20.0174593142685,23.4923,23.47844
1237667430093488288,140.010686497758,20.0781480229235,19.89794,19.64356
1237667209974448712,140.011074894392,20.9763084088396,22.70029,22.85815
1237667430093554008,140.012379856521,20.1469377004989,23.12733,22.86493
1237667209974448933,140.013140656439,20.9086701836405,23.96772,24.16748
1237667430093488949,140.013238349062,20.0312906376057,24.73975,25.34514


Pero, ¡atención!, el método submit() cierra la sesión, por lo que, para enviar otro SQL habrá que repetir el br.open() y el br.select()

A continuación, y con el fin de poder tratar los datos con más facilidad, lo más aconsejable es generar un dataframe de Pandas. Observemos que la primera línea debe ser descartada, y la segunda fila contiene los nombres de las columnas, por lo que la mantendremos en el dataframe

In [13]:
file_like = StringIO(response.get_data())
df =pd.read_csv(file_like, skiprows = 1) # saltamos la primera línea
df
Out[13]:
objID ra dec modelMag_u modelMag_g
0 1237667293189833237 140.000265 20.352817 23.27699 23.07312
1 1237667293189833455 140.001622 20.429801 23.79515 24.28631
2 1237667293189832757 140.003652 20.254631 24.70360 24.82169
3 1237667430093553674 140.004910 20.172000 24.04017 24.81654
4 1237667430093488938 140.009036 20.017459 23.49230 23.47844
5 1237667430093488288 140.010686 20.078148 19.89794 19.64356
6 1237667209974448712 140.011075 20.976308 22.70029 22.85815
7 1237667430093554008 140.012380 20.146938 23.12733 22.86493
8 1237667209974448933 140.013141 20.908670 23.96772 24.16748
9 1237667430093488949 140.013238 20.031291 24.73975 25.34514

A partir de aquí podríamos renombrar las columnas dandoles un nombre más a nuestro gusto, y calcular una nueva columna como diferencia de las columnas u y g, la cual nos indicará el color del astro.

In [14]:
df.columns = ['objID','ra','dec','u','g']
df['u-g'] = df['u']-df['g']
df
Out[14]:
objID ra dec u g u-g
0 1237667293189833237 140.000265 20.352817 23.27699 23.07312 0.20387
1 1237667293189833455 140.001622 20.429801 23.79515 24.28631 -0.49116
2 1237667293189832757 140.003652 20.254631 24.70360 24.82169 -0.11809
3 1237667430093553674 140.004910 20.172000 24.04017 24.81654 -0.77637
4 1237667430093488938 140.009036 20.017459 23.49230 23.47844 0.01386
5 1237667430093488288 140.010686 20.078148 19.89794 19.64356 0.25438
6 1237667209974448712 140.011075 20.976308 22.70029 22.85815 -0.15786
7 1237667430093554008 140.012380 20.146938 23.12733 22.86493 0.26240
8 1237667209974448933 140.013141 20.908670 23.96772 24.16748 -0.19976
9 1237667430093488949 140.013238 20.031291 24.73975 25.34514 -0.60539

Resumen

Una vez visto el fundamento de la utilización del módulo mechanize y la creación de un dataframe de Pandas, podemos definir una función para agilizar el proceso de nuevos SQL

In [15]:
def SDSS_select(sql):
    '''input: string con una sentencia SQL válida
    output: un dataframe de Pandas
    '''
    br.open(url)
    br.select_form(name="sql")
    br['cmd'] = sql
    br['format']=['csv']
    response = br.submit()
    file_like = StringIO(response.get_data())
    return pd.read_csv(file_like,  skiprows=1)

Los pasos a seguir serán los siguientes:

  • Las instrucciones siguientes se ejecutarán solo la primera vez:
In [16]:
# URL de la página de búsquedas de la base de datos DR10
url = "http://skyserver.sdss3.org/dr10/en/tools/search/sql.aspx"
  • Preparamos un string con nuestro SQL
In [17]:
sql = ('SELECT TOP 10 '
'objID, ra, dec, modelMag_u, modelMag_g, modelMag_r, modelMag_i, modelMag_z '
'FROM  Star '
'WHERE ra BETWEEN 150 and 152 AND dec BETWEEN 30 and 31 AND clean = 1')
  • Llamamos a la función, obteniendo un dataframe de Pandas como retorno
In [18]:
df = SDSS_select(sql)

Y ya tenemos listo nuestro dataframe

In [19]:
df
Out[19]:
objID ra dec modelMag_u modelMag_g modelMag_r modelMag_i modelMag_z
0 1237664869216289142 150.000355 30.732103 23.06350 22.79704 21.60797 21.22958 21.32821
1 1237665099003593049 150.000483 30.591703 22.71077 22.14142 21.63762 21.84505 21.44720
2 1237665098466656334 150.000711 30.105872 20.72021 18.12753 16.79675 16.08280 15.69484
3 1237665098466656330 150.000754 30.097463 22.66031 19.98154 18.61721 17.61466 17.07129
4 1237664869216289439 150.000985 30.748886 25.00283 23.08974 22.20838 21.99796 21.30983
5 1237664869216288905 150.001134 30.829336 21.41318 20.29182 19.80290 19.55658 19.49337
6 1237665129067840138 150.001698 30.483674 23.61431 22.05697 20.63076 20.13519 19.81597
7 1237665098466657196 150.002180 30.248386 25.05051 23.19651 21.70999 20.04427 19.07293
8 1237665129067840349 150.002215 30.429241 23.51003 24.00885 21.71184 21.25962 20.59406
9 1237665098466656342 150.002250 30.174147 20.84485 18.57332 17.56038 17.15493 16.93803

Aquí finalizo este post, pero el método de acceso a la base de datos del SDSS que hemos visto aquí lo utilizaré en futuras entradas para analizar, en base a datos reales, diversas propiedades de los objetos celestes.

No hay comentarios:

Publicar un comentario