swifttools.ukssdc.query
moduleJupyter notebook version of this page
Latest version v1.0, released in swifttools v3.0
The query
module provides tools to query various catalogues held at the UKSSDC (and a couple of others) and provides wrappers to get data for the objects we find. There are two types of query supported, a cone search and a more complex search applying filters to different fields. These are analogous to the 'simple' and 'advanced' searches available on the website.
Unlike the the data
module (which provided functions to get specific products), the query
module provided classes: we create query objects and then manipulate and use these to perform our query and play with the results. There are three query classes provided:
Whichever class you want, I strongly advise you to read this page first. Although it deals with the ObsQuery
class, that class only contains functionality common to all of the classes, and so all the common concepts are introduced here, and not necessarily repeated on the other pages.
The query
module also provides access to data or data products, by wrapping functions from the data module.I will not go into much detail about those functions and what they produce, since this has been covered in the relevant pages for the data module already. One crucial point, however, does need discussing: how the query module makes any requested data available to you (if you haven't read about the data module, ignore this point because it will just confuse you - you'll see how we get data shortly).
In the data module the default behaviour when getting a product was to download files to disk, but you could instead request the data be returned from the function so you could capture it in a variable. For the query module, the requested data are always stored in variables inside your query object. You can still also request that they be saved to disk, or returned from the function that got them (using the same returnData
and saveData
arguments as in the data module) but these are not done by default.
In this notebook we will demonstrate the query interface for observation data, which is quite straightforward and also gets us familiar with the module syntax.
We will import the query module as uq
:
import swifttools.ukssdc.query as uq
ObsQuery
class
ObsQuery
ClassThe ObsQuery
class allows us to query the Swift observation database, it is the API equivalent of this UKSSDC webpage. The only functionality it provides is that which is common to the entire swifttools.ukssdc.query
module and all subclasses, so it's the perfect one to use as an introduction.
As discussed above, this module is built around classes, so the first thing we have to do is create an ObsQuery object:
q = uq.ObsQuery(silent=False)
Resetting query details
I set silent=False
because in an interactive case like this, it can be helpful to get some textual feedback. If you want even more feedback you can set verbose=True
. These can be set in the constructor, or via simple calls:
q.verbose = True
q.verbose = False # Turn it off again!
There are several database tables relating to Swift observations. Just like the website, the API gives you the most useful (I think) one by default, but of course, you can change this. But how? Well, first, we want to know what tables we have to choose from, and this is stored in the tables
variable of our query object:
q.tables
('swiftmastr', 'swiftxrlog', 'swiftbalog', 'swiftuvlog', 'swifttdrss')
We can also check which one is currently selected, or change it, via the table
variable:
q.table
'swiftmastr'
q.table = 'swiftxrlog'
Resetting query details
You'll notice the cell that changed the table warned us that it was resetting the query details. This only appeared because we have silent=False
and don't worry about it too much now. Basically it is warning us that any filters set or results retrieved (all covered below) have been wiped because we changed table. We could also have set the table we wanted in the constructor:
q = uq.ObsQuery(table='swiftbalog',
silent=False
)
q.table
Resetting query details
'swiftbalog'
I won't keep saying this but I'll remind you here: ObsQuery
only contains behaviour common to the entire query
module, so the syntax for checking and changing tables is the same for the GRBQuery
and SXPSQuery
modules (and for any others I may add in the future).
A simple search is just a cone search, and rather than pontificating, let's just demonstrate. I will stick with the default table, "swiftmastr" for all the demonstrations below.
q = uq.ObsQuery(silent=False)
q.addConeSearch(name='GK Per', radius=300, units='arcsec')
q.submit()
Resetting query details
Need to get the metadata to check the query is valid.
Calling DB look-up for rows 0 -- 1000
Resolved 'GK Per' as (52.80004119305, 43.90429720867) via SIMBAD
Received 145 rows.
That was pretty straightforward wasn't it? We introduced two functions here, addConeSearch()
and submit()
. The latter is very easy: it takes no arguments and just submits our query object for execution.
addConeSearch()
should also be pretty clear, but I'll elaborate on its arguments in a moment.
Because we disabled silent mode, we got a bit of information about what was going on, and we can see, firstly, that "GK Per" was resolved into coordinates (thank you SIMBAD), and secondly, that we got 145 results. But where are those results? They are stored inside your query object (q
) and the variable holding them is cunningly named results
. This is a pandas DataFrame
and we can have a look at it:
q.results
_r | name | target_id | ra | decl | roll_angle | start_time | stop_time | obs_segment | obsid | bat_exposure | xrt_exposure | xrt_expo_wt | xrt_expo_pc | uvot_exposure | ra_s | ra_apy | decl_s | decl_apy | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 10.171878 | GKPer | 30842 | 52.796556 | 43.903002 | 254.995416 | 2010-03-09T00:11:00 | 2010-03-09T01:29:57 | 27 | 00030842027 | 1483 | 1717.359 | 3.086 | 1714.273 | 1698.728 | +03h 31m 11.17s | 52d47m47.6016s | +43d 54m 10.8s | 43d54m10.8072s |
1 | 12.212516 | GKPer | 30842 | 52.803476 | 43.901977 | 257.760120 | 2010-03-08T19:18:00 | 2010-03-08T23:09:41 | 24 | 00030842024 | 2031 | 1978.567 | 12.484 | 1966.083 | 1958.950 | +03h 31m 12.83s | 52d48m12.5136s | +43d 54m 7.1s | 43d54m07.1172s |
2 | 15.595530 | GKPeroffset1 | 31653 | 52.805844 | 43.905432 | 239.081804 | 2010-03-29T21:23:00 | 2010-03-29T23:53:15 | 44 | 00031653044 | 600 | 88.973 | 88.973 | 0.000 | 85.682 | +03h 31m 13.40s | 52d48m21.0384s | +43d 54m 19.6s | 43d54m19.5552s |
3 | 17.510511 | GKPeroffset1 | 31653 | 52.793651 | 43.905866 | 239.071668 | 2010-03-12T11:49:59 | 2010-03-12T12:44:19 | 11 | 00031653011 | 301 | 39.563 | 1.963 | 37.600 | 39.575 | +03h 31m 10.48s | 52d47m37.1436s | +43d 54m 21.1s | 43d54m21.1176s |
4 | 21.057130 | GKPer | 30842 | 52.792533 | 43.902073 | 270.123645 | 2007-01-30T01:37:30 | 2007-01-30T07:19:01 | 7 | 00030842007 | 1054 | 986.560 | 19.631 | 966.929 | 986.906 | +03h 31m 10.21s | 52d47m33.1188s | +43d 54m 7.5s | 43d54m07.4628s |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
140 | 269.556139 | GKPer | 30842 | 52.724707 | 43.955901 | 220.787476 | 2015-04-07T01:17:59 | 2015-04-07T13:43:55 | 73 | 00030842073 | 1271 | 1264.049 | 1264.049 | 0.000 | 1256.760 | +03h 30m 53.93s | 52d43m28.9452s | +43d 57m 21.2s | 43d57m21.2436s |
141 | 281.324633 | GKPeroffset1 | 31653 | 52.697974 | 43.930776 | 238.993264 | 2010-03-25T13:09:00 | 2010-03-25T15:44:59 | 36 | 00031653036 | 600 | 103.897 | 103.897 | 0.000 | 97.012 | +03h 30m 47.51s | 52d41m52.7064s | +43d 55m 50.8s | 43d55m50.7936s |
142 | 287.191916 | GKPer | 30842 | 52.692720 | 43.923969 | 245.243652 | 2015-03-26T19:32:59 | 2015-03-26T21:07:46 | 62 | 00030842062 | 1227 | 1220.134 | 4.095 | 1216.039 | 1219.336 | +03h 30m 46.25s | 52d41m33.792s | +43d 55m 26.3s | 43d55m26.2884s |
143 | 292.861073 | GKPer | 30842 | 52.699655 | 43.941577 | 249.143502 | 2015-03-16T13:42:58 | 2015-03-16T14:36:55 | 42 | 00030842042 | 970 | 958.988 | 6.226 | 952.762 | 961.740 | +03h 30m 47.92s | 52d41m58.758s | +43d 56m 29.7s | 43d56m29.6772s |
144 | 299.472390 | GKPer | 30842 | 52.792423 | 43.987303 | 241.157935 | 2015-04-02T03:08:59 | 2015-04-02T04:02:27 | 68 | 00030842068 | 1058 | 1054.447 | 1054.447 | 0.000 | 1050.411 | +03h 31m 10.18s | 52d47m32.7228s | +43d 59m 14.3s | 43d59m14.2908s |
145 rows × 19 columns
You can explore this at your leisure, but let me highlight one point regarding coordinates. In the databases we query, coordinates are stored in decimal degrees (J2000), but this may not be how you want them. So, when you perform a query that gets coordinates, the query
module will do a bit of extra work. It identifies all of the coordinate columns and creates sexagesimal versions of the coordinates to (in the format of strings). To identify these, "_s" is appended to the column name (so in the above, "ra" and "decl" were part of the database, and "ra_s", "decl_s" have been added. Also, if you have the astropy
module installed then the coordinates will be converted into astropy.coordinate.Angle
objects and identied by "_apy" ("ra_apy" and "decl_apy" in the above.
When we executed the query above, we were told how the name supplied had been resolved; but only because we set silent=False
, and not in a way that we could have readily captured in our script. Despair not, the details of the name resolution are also in class variables:
q.resolvedInfo
"Resolved 'GK Per' as (52.80004119305, 43.90429720867) via SIMBAD"
Or indeed:
print(q.resolvedRA)
print(q.resolvedDec)
52.80004119305
43.90429720867
We're going to go back and look at addConeSearch()
a bit more and explore some of its syntax, but if we just ran q.addConeSearch()
again now, we'd get an error telling us our query was locked. This is because when a query is submitted it is locked to prevent ending up in a confused state (a trivial example; imagine you ran the query above and then ran q.addConeSearch(name='FO Aqr')
but due to an error, didn't submit it; it would be easy to mistakenly think that q.results
was related to the FO Aqr search. It isn't).
So, if we want to do another query we either need to make a new ObsQuery
object, or reset the one we have. Let's do the latter:
q.reset()
Resetting query details
Now we can make any changes we like. Before I move on I'll note that q.reset()
has some options, you don't have to reset literally everything, but you can read about those via the help
command if you want.
So, now we can do another cone search if we want. Before we do, let's look at the arguments that this function takes. You could do this via help (q.addConeSearch)
but I'll be nice and elaborate here.
A cone search needs to know two things:
The latter is easy, it is managed by two arguments:
radius
- a number giving the radius.units
- the units of radius
, must be 'arcsec', 'arcmin' or 'deg' (default: 'arcsec').The centre of the cone can be specified in a few ways, via these arguments:
name
- a string giving an object name which we will attempt to resolve.position
- a free-form string giving the position, which we will attempt to parse.ra
and dec
- Two arguments that can either be float
s or astropy.coordinates.Angle
objects.You should only provide one of these arguments (OK, two if ra
and dec
) or you will get an error. name
was used above. position
is a free-form string and we have tried to accept almost any sane way in which you may enter coordinates (provided they are in J2000). So, all of the examples below will work:
q.addConeSearch(ra=123.456, dec=-43.221, radius=1, units='deg')
q.addConeSearch(position='12 13 15, -15 16 17', radius=12, units='arcmin')
from astropy.coordinates import Angle
ra = Angle('12h 13m 14s')
dec = Angle('-13d 14m 15s')
q.addConeSearch(ra=ra, dec=dec, radius=300, units='arcsec')
If you provided a name
or position
, it will be resolved when the query is submitted, so you can check the details of the resolution using the q.resolvedRA
etc. variables already introduced. If you supplied ra
and dec
you can actually read these back; for example if you don't trust astropy
as used above, we can check that the correct decimal values were extracted:
print(q.coneRA)
print(q.coneDec)
183.3083333333333
-13.237499999999999
Note that these variables are read-only so don't try to edit them. If you made a mistake you can use q.editConeSearch()
, which takes exactly the same arguments as addConeSearch()
.
We can also change our minds completely and remove the cone search settings from our query:
q.removeConeSearch()
That's all there is to setting up the cone search aspect of the query.
When we ran our demo query above, we got a lot of columns containing data, but what if we didn't want them all? Or if we wanted one not included? The default set of columns returned is not exhaustive. Well, of course, you can change what set of columns you get as we'll now discuss. First of all, it would be nice to know what we are going to get by default. These are stored in the defaultCols
variable:
q = uq.ObsQuery(silent=False)
q.defaultCols
Resetting query details
Need to get the metadata.
['name',
'target_id',
'ra',
'decl',
'roll_angle',
'start_time',
'stop_time',
'obs_segment',
'obsid',
'bat_exposure',
'xrt_exposure',
'xrt_expo_wt',
'xrt_expo_pc',
'uvot_exposure']
As you can see, this is just a list, and it's a list of column names. Which is nice and all, but what actually are these columns, and what extra ones are available?
Because we have silent=False
you can see from the above that in order to identify the default columns, the Python module grabbed some metadata. It is this metadata which tells us all about the table. Let's take a look at it:
q.metadata
ColName | Class | Description | Type | LongDescription | IsObsCol | IsTargetCol | |
---|---|---|---|---|---|---|---|
0 | name | BASIC | Designation of the NFI Pointed Source | TXT | This is the name of the pointed target. For GR... | 0 | 0 |
1 | orig_target_id | MISC | Trigger Number as Originally Assigned | INT | This is a numerical value assigned automatical... | 0 | 0 |
2 | target_id | BASIC | Unique Trigger Number with Any Degeneracy Removed | INT | This is a unique numerical value assigned to e... | 0 | 1 |
3 | ra | BASIC | Right Ascension (Pointing Position) | COORDH | Right Ascension of the pointing position. Note... | 0 | 0 |
4 | decl | BASIC | Declination (Pointing Position) | COORDD | Declination of the pointing position. Note tha... | 0 | 0 |
5 | roll_angle | BASIC | Roll Angle (degree) | FLOAT | Roll angle of the observation given in degrees. | 0 | 0 |
6 | start_time | BASIC | Start Time of the Observation | TXT | Start time of the observation. Note that the d... | 0 | 0 |
7 | stop_time | BASIC | Stop Time of the Observation | TXT | Stop time of the observation. For an Automatic... | 0 | 0 |
8 | orig_obs_segment | MISC | Observation Segment as Originally Assigned | INT | The Swift observation strategy is similar to a... | 0 | 0 |
9 | obs_segment | BASIC | True Observation Segment (Corrected Value) | INT | Observation Segment. The Swift observation str... | 0 | 0 |
10 | orig_obsid | MISC | Observation Number as Originally Assigned (Ori... | TXT | This parameter contains a numeric value that s... | 0 | 0 |
11 | obsid | BASIC | Unique Observation Number (Target_ID + Obs_Seg... | TXT | This parameter contains a numeric value that u... | 1 | 0 |
12 | bat_exposure | BASIC BAT | Effective Exposure Event and Survey modes with... | FLOAT | The BAT exposure in seconds on source. The BAT... | 0 | 0 |
13 | xrt_exposure | BASIC XRT | Effective Exposure on Source All XRT Modes | FLOAT | The XRT exposure in seconds on source. The XRT... | 0 | 0 |
14 | xrt_expo_wt | BASIC XRT | Effective Exposure on Source Windowed Timing ... | FLOAT | The XRT exposure on source when the Windowed T... | 0 | 0 |
15 | xrt_expo_pc | BASIC XRT | Effective Exposure on Source Photon Counting X... | FLOAT | The XRT exposure on source when the Photon Cou... | 0 | 0 |
16 | uvot_exposure | BASIC UVOT | Effective Exposure on Source All UVOT Filters ... | FLOAT | The UVOT exposure in seconds on source. The UV... | 0 | 0 |
17 | xrt_expo_lr | XRT | Effective Exposure on Source Low Rate Photodio... | FLOAT | The XRT exposure on source when the Low-Rate P... | 0 | 0 |
18 | xrt_expo_pu | XRT | Effective Exposure on Source Piled-up Photodio... | FLOAT | The XRT exposure on source when the Piled-Up P... | 0 | 0 |
19 | xrt_expo_im | XRT | Effective Exposure on Source Image Modes | FLOAT | The XRT exposure on source when the IMAGE (IM)... | 0 | 0 |
20 | uvot_expo_uu | UVOT | Effective Exposure on Source U UVOT Filter | FLOAT | The UVOT exposure on source when the U filter ... | 0 | 0 |
21 | uvot_expo_bb | UVOT | Effective Exposure on Source B UVOT Filter | FLOAT | The UVOT exposure on source when the B filter ... | 0 | 0 |
22 | uvot_expo_vv | UVOT | Effective Exposure on Source V UVOT Filter | FLOAT | The UVOT exposure on source when the V filter ... | 0 | 0 |
23 | uvot_expo_w1 | UVOT | Effective Exposure on Source UVW1 UVOT Filter | FLOAT | The UVOT exposure on source when the UVW1 filt... | 0 | 0 |
24 | uvot_expo_w2 | UVOT | Effective Exposure on Source UVW2 UVOT Filter | FLOAT | The UVOT exposure on source when the UVW2 filt... | 0 | 0 |
25 | uvot_expo_m2 | UVOT | Effective Exposure on Source UVM2 UVOT Filter | FLOAT | The UVOT exposure on source when the UVM2 filt... | 0 | 0 |
26 | uvot_expo_wh | UVOT | Effective Exposure on Source WHITE UVOT Filter | FLOAT | The UVOT exposure on source when the White fil... | 0 | 0 |
27 | uvot_expo_gu | UVOT | Effective Exposure on Source UGRISM UVOT Filter | FLOAT | The UVOT exposure on source when the UGRISM fi... | 0 | 0 |
28 | uvot_expo_gv | UVOT | Effective Exposure on Source VGRISM UVOT Filter | FLOAT | The UVOT exposure on source when the VGRISM fi... | 0 | 0 |
29 | uvot_expo_mg | UVOT | Effective Exposure on Source Magnifier UVOT Fi... | FLOAT | The UVOT exposure on source when the Magnifier... | 0 | 0 |
30 | uvot_expo_bl | UVOT | Effective Exposure Blocked Filter | FLOAT | None | 0 | 0 |
31 | bat_expo_ev | BAT | Effective Exposure Event mode | FLOAT | The BAT exposure on source when the EVENT mode... | 0 | 0 |
32 | bat_expo_sv | BAT | Effective Exposure Survey mode | FLOAT | BAT exposure on source when the SURVEY mode wa... | 0 | 0 |
33 | bat_expo_rt | BAT | Effective Exposure Rate mode | FLOAT | BAT exposure on source when the RATE mode was ... | 0 | 0 |
34 | bat_expo_mt | BAT | Effective Exposure Masktag mode | FLOAT | Cumulative BAT exposure of all sources tagged ... | 0 | 0 |
35 | bat_expo_pl | BAT | Effective Exposure pulsar mode | FLOAT | BAT exposure on source when the PULSAR mode wa... | 0 | 0 |
36 | bat_no_masktag | BAT | Number of sources with mask tag rate within th... | INT | Number of sources for which the MASKTAG rate m... | 0 | 0 |
37 | archive_date | MISC | Public date | TXT | The archive_date is the date when the data are... | 0 | 0 |
38 | soft_version | MISC | Software version used in the pipeline | TXT | None | 0 | 0 |
39 | processing_date | MISC | Date of Processing | TXT | This records the date when the data were proce... | 0 | 0 |
40 | processing_version | MISC | Version of Processing Scrip | TXT | This records the version of the processing scr... | 0 | 0 |
41 | num_processed | MISC | Number of Times a Sequence Has Been Processed | INT | This records the number of times a data set ha... | 0 | 0 |
42 | cycle | MISC | Proposal cycle number | INT | The proposal cycle number of the observation. | 0 | 0 |
43 | pi | MISC | PI Last Name | TXT | Principal Investigator. If the Swift Announcem... | 0 | 0 |
44 | att_flag | MISC | Attitude flag | TXT | Attitude flag. | 0 | 0 |
Table metadata is made available in the form of a pandas DataFrame
. The exact columns in this do differ slightly for the different tables (and modules; 'LongDescription' is only present for the ObsQuery
class), and not all of them are relevant for you - some are needed internally by the Python module, and some are needed by the web interfaces to the databases. The columns you really care about are "ColName" and "Description" which should provide enough details for you to choose whether the default columns are enough, or if you want to edit them.
So, knowing what columns exist, we can now decide which ones we want to obtain, if we didn't just want the default. We add columns using the addCol()
function. This takes either *
(to get everything), a column name, or a list/tuple of column names. So:
q.addCol('name')
q.colsToGet
['name']
As shown above, the colsToGet
variable (which you cannot edit directly) tells you what columns are selected, and you may have noticed that now we've explicitly said what we want, all the default columns are not there. We can easily add them back in though:
q.addCol(q.defaultCols)
q.colsToGet
Cannot add column name; it is already selected.
['name',
'target_id',
'ra',
'decl',
'roll_angle',
'start_time',
'stop_time',
'obs_segment',
'obsid',
'bat_exposure',
'xrt_exposure',
'xrt_expo_wt',
'xrt_expo_pc',
'uvot_exposure']
You note that we got a warning because we tried to add an existing column in, but it was just a warning and did not stop the other columns being added. We can also remove columns with removeCol
, or removeAllCols
. So here are some examples:
q.removeCol('name')
q.colsToGet
['target_id',
'ra',
'decl',
'roll_angle',
'start_time',
'stop_time',
'obs_segment',
'obsid',
'bat_exposure',
'xrt_exposure',
'xrt_expo_wt',
'xrt_expo_pc',
'uvot_exposure']
q.removeCol(('obsid', 'stop_time'))
q.colsToGet
['target_id',
'ra',
'decl',
'roll_angle',
'start_time',
'obs_segment',
'bat_exposure',
'xrt_exposure',
'xrt_expo_wt',
'xrt_expo_pc',
'uvot_exposure']
q.addCol(['cycle', 'soft_version']+q.defaultCols)
Cannot add column target_id; it is already selected.
Cannot add column ra; it is already selected.
Cannot add column decl; it is already selected.
Cannot add column roll_angle; it is already selected.
Cannot add column start_time; it is already selected.
Cannot add column obs_segment; it is already selected.
Cannot add column bat_exposure; it is already selected.
Cannot add column xrt_exposure; it is already selected.
Cannot add column xrt_expo_wt; it is already selected.
Cannot add column xrt_expo_pc; it is already selected.
Cannot add column uvot_exposure; it is already selected.
The latter was a slightly gratuitous demonstration of a way that you can add the default columns, and some others, all in one go. We can also remove the whole lot in one go:
q.removeAllCols()
q.colsToGet
If we were to submit this now we would not get an empty result set, but rather the default columns, which are always request if colsToGet
is empty.
If you've been reading everything very carefully, you may be wondering why the results from our demonstration cone search contained the column _r
, which has not appeared anywhere in the metadata or our requests. _r
is a special column only created for cone searches, and it contains the angular distance of each row from the centre of the search. The units are whatever was given as the units
argument to addConeSearch()
(arcsec by default).
'Advanced' searches are those where we select data based on filters applied to specific columns. This is actually fairly simple, we just define a series of filters to apply to the query and then submit it. These can be in addition to, or instead of the cone search. There are a few things to note before we start:
This will hopefully all make sense as we go on.
We add filters using the imaginatively-named addFilter()
function, which takes a single argument, a filter definition. A filter definition can either be a dict
or a list
and has the following components:
I will unpack these in more detail but an example is probably a better helper so, here are two filters created using the two approaches:
filter1 = ('xrt_exposure', '>', 1000, 'OR', '<', 200)
filter2 = {
"colName": "ra",
"filter": ">",
"val": 123,
"combiner": "and" ,
"filter2": "<",
"val2": 200
}
If these filters were converted to SQL they would be written as
xrt_exposure>1000 OR xrt_exposure<200
ra>123 AND ra<200
And if we submitted this query they would combine as:
(xrt_exposure>1000 OR xrt_exposure<200) AND (ra>123 AND ra<200)
If we wanted to create a query using these filters we would do:
q = uq.ObsQuery(silent=False)
q.addFilter(filter1)
q.addFilter(filter2)
Resetting query details
Need to get the metadata.
A filter doesn't have to have all of the elements we used above. You may not want the second part of the filter, so everything from combiner
onwards can simply be omitted. Also, some of the filters do not require arguments, so in that case, val
(or val2
) can be left out. Conversely, the 'BETWEEN' filter requires two arguments and so val
must take a 2-element tuple/list.
Some more examples:
q.removeAllFilters()
q.addFilter ( ('xrt_exposure', '<', 2000))
q.addFilter ( ('ra', 'BETWEEN', [100,200]))
q.addFilter ({
'colName': 'target_id',
'filter': 'IS NULL',
'combiner': 'or',
'filter2': '<',
'val2': 10000
})
Here I've introduced the removeAllFilters()
function (you can probably guess what it does) and a few more examples of adding filters, including some where we don't need all elements of the filter definition.
The following values are permitted for the 'filter' and 'filter2' keys:
All of these require a single value, except for 'BETWEEN', which requires 2, and 'IS NULL' and 'IS NOT NULL' which take no values.
Having added filters we can check them:
q.showFilters()
0: xrt_exposure < 2000
1: ra BETWEEN [100, 200]
2: target_id IS NULL OR < 10000
The outputs as you can see are strings.
We can also remove specific filters by their index. Please bear in mind indices will change when you remove a filter!
q.removeFilter(1)
0: xrt_exposure < 2000
1: target_id IS NULL OR < 10000
Because silent=False
the above function printed out the revised set of filters, if it was True
you'd have to do this yourself with another showFilters()
call.
Right, enough messing around, let's do an actual query. I'm going to do both a cone and advanced search together here, just to prove you can (and to limit how many rows we get), but of course, you don't have to.
q=uq.ObsQuery(silent=False)
q.addConeSearch(name='GK Per', radius=12, units='arcmin')
q.addFilter( ('xrt_exposure', '>', 3000))
q.isValid()
Resetting query details
Need to get the metadata.
True
Here, we have created a query, requested a cone search of 12' radius, centred on "GK Per", and we have asked to only get those rows where the "xrt_exposure" field is above 3000. I also made use of a function, isValid()
. This just checks that the syntax is OK and we haven't done anything silly. Let's submit the query:
q.submit()
Calling DB look-up for rows 0 -- 1000
Resolved 'GK Per' as (52.80004119305, 43.90429720867) via SIMBAD
Received 21 rows.
Again, because we were not silent we got some information printed, but this is also available in variables. I have already discussed accessing details of the name resolution, but how could I find out that 21 rows were returned without having to read from the screen? Like this:
q.numRows
21
Or indeed:
len(q.results)
21
if you prefer. They should be the same. And let's just prove that our filters worked:
q.results
_r | name | target_id | ra | decl | roll_angle | start_time | stop_time | obs_segment | obsid | bat_exposure | xrt_exposure | xrt_expo_wt | xrt_expo_pc | uvot_exposure | ra_s | ra_apy | decl_s | decl_apy | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0.421999 | GKPer | 30842 | 52.793185 | 43.899291 | 280.915675 | 2007-01-17T03:10:01 | 2007-01-17T13:41:49 | 5 | 00030842005 | 6053 | 6078.284 | 60.784 | 6017.500 | 5862.453 | +03h 31m 10.36s | 52d47m35.466s | +43d 53m 57.4s | 43d53m57.4476s |
1 | 0.483022 | GKPer | 30842 | 52.811212 | 43.904125 | 284.011915 | 2007-01-09T08:32:01 | 2007-01-09T12:38:45 | 4 | 00030842004 | 4896 | 4846.696 | 25.133 | 4821.563 | 4844.257 | +03h 31m 14.69s | 52d48m40.3632s | +43d 54m 14.9s | 43d54m14.85s |
2 | 0.515095 | GKPer | 30842 | 52.788651 | 43.906818 | 266.451771 | 2007-02-08T03:47:01 | 2007-02-08T22:21:13 | 9 | 00030842009 | 5374 | 5278.844 | 28.914 | 5249.930 | 5271.033 | +03h 31m 9.28s | 52d47m19.1436s | +43d 54m 24.5s | 43d54m24.5448s |
3 | 0.916885 | GKPer | 30842 | 52.779428 | 43.907897 | 292.140501 | 2007-01-02T12:43:01 | 2007-01-02T16:47:21 | 3 | 00030842003 | 4788 | 4733.461 | 41.147 | 4692.314 | 4735.406 | +03h 31m 7.06s | 52d46m45.9408s | +43d 54m 28.4s | 43d54m28.4292s |
4 | 0.962148 | GKPer | 30842 | 52.778149 | 43.901410 | 269.888920 | 2007-02-04T14:50:01 | 2007-02-04T20:23:10 | 8 | 00030842008 | 4046 | 3979.563 | 43.122 | 3936.441 | 3978.030 | +03h 31m 6.76s | 52d46m41.3364s | +43d 54m 5.1s | 43d54m05.076s |
5 | 1.161197 | GKPer | 30842 | 52.773182 | 43.904518 | 276.237384 | 2007-01-23T02:08:01 | 2007-01-23T07:50:49 | 6 | 00030842006 | 5872 | 5806.444 | 27.112 | 5779.332 | 5804.662 | +03h 31m 5.56s | 52d46m23.4552s | +43d 54m 16.3s | 43d54m16.2648s |
6 | 1.498005 | GK_Per | 81445 | 52.829137 | 43.890741 | 63.483317 | 2015-09-09T09:14:58 | 2015-09-09T18:04:10 | 2 | 00081445002 | 4458 | 4419.383 | 0.040 | 4419.343 | 4350.644 | +03h 31m 18.99s | 52d49m44.8932s | +43d 53m 26.7s | 43d53m26.6676s |
7 | 1.589266 | GKPer | 30842 | 52.763626 | 43.900668 | 252.039706 | 2007-02-12T02:16:00 | 2007-02-12T14:23:37 | 11 | 00030842011 | 3159 | 3023.056 | 59.527 | 2963.529 | 3023.295 | +03h 31m 3.27s | 52d45m49.0536s | +43d 54m 2.4s | 43d54m02.4048s |
8 | 1.642341 | GKPer | 30842 | 52.770293 | 43.921326 | 308.921791 | 2006-12-20T16:12:19 | 2006-12-20T18:34:48 | 1 | 00030842001 | 4022 | 3980.145 | 33.504 | 3946.641 | 3988.765 | +03h 31m 4.87s | 52d46m13.0548s | +43d 55m 16.8s | 43d55m16.7736s |
9 | 1.798257 | GKPer | 30842 | 52.837858 | 43.916788 | 300.429716 | 2006-12-26T02:26:01 | 2006-12-26T08:07:50 | 2 | 00030842002 | 4607 | 4540.904 | 30.284 | 4510.620 | 4538.745 | +03h 31m 21.09s | 52d50m16.2888s | +43d 55m 0.4s | 43d55m00.4368s |
10 | 2.034272 | GKPeroffset1 | 31653 | 52.802362 | 43.870434 | 238.001562 | 2010-04-01T02:27:00 | 2010-04-01T19:52:48 | 47 | 00031653047 | 3481 | 5341.812 | 5341.812 | 0.000 | 5288.652 | +03h 31m 12.57s | 52d48m08.5032s | +43d 52m 13.6s | 43d52m13.5624s |
11 | 2.234021 | GKPeroffset1 | 31653 | 52.800838 | 43.867068 | 239.993197 | 2010-04-05T02:45:59 | 2010-04-05T20:13:48 | 49 | 00031653049 | 4562 | 5341.801 | 5341.801 | 0.000 | 5286.456 | +03h 31m 12.20s | 52d48m03.0168s | +43d 52m 1.4s | 43d52m01.4448s |
12 | 2.501833 | GKPer | 45767 | 52.743700 | 43.913840 | 249.208788 | 2013-03-20T01:02:59 | 2013-03-21T01:20:22 | 5 | 00045767005 | 4143 | 4121.740 | 63.538 | 4058.202 | 4087.774 | +03h 30m 58.49s | 52d44m37.32s | +43d 54m 49.8s | 43d54m49.824s |
13 | 2.570384 | GKPer | 30842 | 52.740645 | 43.906271 | 262.071536 | 2007-02-16T02:39:01 | 2007-02-16T08:20:54 | 12 | 00030842012 | 6303 | 6238.041 | 82.605 | 6155.436 | 6237.197 | +03h 30m 57.75s | 52d44m26.322s | +43d 54m 22.6s | 43d54m22.5756s |
14 | 2.606862 | GKPer | 30842 | 52.745736 | 43.923199 | 253.806039 | 2007-03-13T00:29:01 | 2007-03-13T11:01:02 | 19 | 00030842019 | 6106 | 5969.147 | 78.192 | 5890.955 | 5981.222 | +03h 30m 58.98s | 52d44m44.6496s | +43d 55m 23.5s | 43d55m23.5164s |
15 | 2.802018 | GKPer | 30842 | 52.745146 | 43.929141 | 260.414911 | 2007-02-26T00:25:01 | 2007-02-26T06:08:24 | 15 | 00030842015 | 6488 | 6403.276 | 32.808 | 6370.468 | 6418.423 | +03h 30m 58.84s | 52d44m42.5256s | +43d 55m 44.9s | 43d55m44.9076s |
16 | 2.875683 | GKPer | 30842 | 52.734551 | 43.912719 | 261.255083 | 2007-02-23T00:06:01 | 2007-02-23T02:36:36 | 14 | 00030842014 | 3277 | 3244.444 | 5.008 | 3239.436 | 3243.491 | +03h 30m 56.29s | 52d44m04.3836s | +43d 54m 45.8s | 43d54m45.7884s |
17 | 2.940585 | GKPer | 30842 | 52.732920 | 43.912266 | 255.973292 | 2007-03-06T04:39:01 | 2007-03-06T14:14:01 | 17 | 00030842017 | 7951 | 7846.457 | 51.297 | 7795.160 | 7848.622 | +03h 30m 55.90s | 52d43m58.512s | +43d 54m 44.2s | 43d54m44.1576s |
18 | 3.344602 | GKPer | 30842 | 52.726982 | 43.922663 | 254.061770 | 2007-03-09T00:16:01 | 2007-03-09T23:26:23 | 18 | 00030842018 | 4570 | 4311.634 | 111.419 | 4200.215 | 4228.598 | +03h 30m 54.48s | 52d43m37.1352s | +43d 55m 21.6s | 43d55m21.5868s |
19 | 3.541167 | GKPer | 30842 | 52.718735 | 43.897146 | 242.945235 | 2007-03-02T12:04:01 | 2007-03-02T22:36:04 | 16 | 00030842016 | 3410 | 3261.669 | 118.117 | 3143.552 | 3288.439 | +03h 30m 52.50s | 52d43m07.446s | +43d 53m 49.7s | 43d53m49.7256s |
20 | 5.582058 | GKPer | 30842 | 52.878237 | 43.978359 | 50.625546 | 2007-09-27T14:04:00 | 2007-09-28T01:29:45 | 20 | 00030842020 | 3089 | 3010.216 | 0.000 | 3010.216 | 0.000 | +03h 31m 30.78s | 52d52m41.6532s | +43d 58m 42.1s | 43d58m42.0924s |
There are one or two further things to discuss: sorting the results, and controlling how many rows we get. Both of these can be done after the query is complete using pandas
functions, or they can be done at query time. Let's start with sorting.
By default, if you did a cone search the results are ordered by increasing distance from the cone centre. If you didn't do a cone search, they are ordered by however they come out of the database. To control this sorting, we use two variables in our class sortCol
and sortDir
. The former is the name of a column in the table (which we can get via q.metadata
, as above), the latter is either "ASC" or "DESC", (for ascending or descending).
Let's have a quick demo, and while I'm here I'll show you that you can unlock a query, rather than fully resetting it, if you just want to change something and resubmit:
q.unlock()
q.sortCol='xrt_exposure'
q.sortDir='DESC'
q.submit()
q.results
Calling DB look-up for rows 0 -- 1000
Resolved 'GK Per' as (52.80004119305, 43.90429720867) via SIMBAD
Received 21 rows.
_r | name | target_id | ra | decl | roll_angle | start_time | stop_time | obs_segment | obsid | bat_exposure | xrt_exposure | xrt_expo_wt | xrt_expo_pc | uvot_exposure | ra_s | ra_apy | decl_s | decl_apy | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2.940585 | GKPer | 30842 | 52.732920 | 43.912266 | 255.973292 | 2007-03-06T04:39:01 | 2007-03-06T14:14:01 | 17 | 00030842017 | 7951 | 7846.457 | 51.297 | 7795.160 | 7848.622 | +03h 30m 55.90s | 52d43m58.512s | +43d 54m 44.2s | 43d54m44.1576s |
1 | 2.802018 | GKPer | 30842 | 52.745146 | 43.929141 | 260.414911 | 2007-02-26T00:25:01 | 2007-02-26T06:08:24 | 15 | 00030842015 | 6488 | 6403.276 | 32.808 | 6370.468 | 6418.423 | +03h 30m 58.84s | 52d44m42.5256s | +43d 55m 44.9s | 43d55m44.9076s |
2 | 2.570384 | GKPer | 30842 | 52.740645 | 43.906271 | 262.071536 | 2007-02-16T02:39:01 | 2007-02-16T08:20:54 | 12 | 00030842012 | 6303 | 6238.041 | 82.605 | 6155.436 | 6237.197 | +03h 30m 57.75s | 52d44m26.322s | +43d 54m 22.6s | 43d54m22.5756s |
3 | 0.421999 | GKPer | 30842 | 52.793185 | 43.899291 | 280.915675 | 2007-01-17T03:10:01 | 2007-01-17T13:41:49 | 5 | 00030842005 | 6053 | 6078.284 | 60.784 | 6017.500 | 5862.453 | +03h 31m 10.36s | 52d47m35.466s | +43d 53m 57.4s | 43d53m57.4476s |
4 | 2.606862 | GKPer | 30842 | 52.745736 | 43.923199 | 253.806039 | 2007-03-13T00:29:01 | 2007-03-13T11:01:02 | 19 | 00030842019 | 6106 | 5969.147 | 78.192 | 5890.955 | 5981.222 | +03h 30m 58.98s | 52d44m44.6496s | +43d 55m 23.5s | 43d55m23.5164s |
5 | 1.161197 | GKPer | 30842 | 52.773182 | 43.904518 | 276.237384 | 2007-01-23T02:08:01 | 2007-01-23T07:50:49 | 6 | 00030842006 | 5872 | 5806.444 | 27.112 | 5779.332 | 5804.662 | +03h 31m 5.56s | 52d46m23.4552s | +43d 54m 16.3s | 43d54m16.2648s |
6 | 2.034272 | GKPeroffset1 | 31653 | 52.802362 | 43.870434 | 238.001562 | 2010-04-01T02:27:00 | 2010-04-01T19:52:48 | 47 | 00031653047 | 3481 | 5341.812 | 5341.812 | 0.000 | 5288.652 | +03h 31m 12.57s | 52d48m08.5032s | +43d 52m 13.6s | 43d52m13.5624s |
7 | 2.234021 | GKPeroffset1 | 31653 | 52.800838 | 43.867068 | 239.993197 | 2010-04-05T02:45:59 | 2010-04-05T20:13:48 | 49 | 00031653049 | 4562 | 5341.801 | 5341.801 | 0.000 | 5286.456 | +03h 31m 12.20s | 52d48m03.0168s | +43d 52m 1.4s | 43d52m01.4448s |
8 | 0.515095 | GKPer | 30842 | 52.788651 | 43.906818 | 266.451771 | 2007-02-08T03:47:01 | 2007-02-08T22:21:13 | 9 | 00030842009 | 5374 | 5278.844 | 28.914 | 5249.930 | 5271.033 | +03h 31m 9.28s | 52d47m19.1436s | +43d 54m 24.5s | 43d54m24.5448s |
9 | 0.483022 | GKPer | 30842 | 52.811212 | 43.904125 | 284.011915 | 2007-01-09T08:32:01 | 2007-01-09T12:38:45 | 4 | 00030842004 | 4896 | 4846.696 | 25.133 | 4821.563 | 4844.257 | +03h 31m 14.69s | 52d48m40.3632s | +43d 54m 14.9s | 43d54m14.85s |
10 | 0.916885 | GKPer | 30842 | 52.779428 | 43.907897 | 292.140501 | 2007-01-02T12:43:01 | 2007-01-02T16:47:21 | 3 | 00030842003 | 4788 | 4733.461 | 41.147 | 4692.314 | 4735.406 | +03h 31m 7.06s | 52d46m45.9408s | +43d 54m 28.4s | 43d54m28.4292s |
11 | 1.798257 | GKPer | 30842 | 52.837858 | 43.916788 | 300.429716 | 2006-12-26T02:26:01 | 2006-12-26T08:07:50 | 2 | 00030842002 | 4607 | 4540.904 | 30.284 | 4510.620 | 4538.745 | +03h 31m 21.09s | 52d50m16.2888s | +43d 55m 0.4s | 43d55m00.4368s |
12 | 1.498005 | GK_Per | 81445 | 52.829137 | 43.890741 | 63.483317 | 2015-09-09T09:14:58 | 2015-09-09T18:04:10 | 2 | 00081445002 | 4458 | 4419.383 | 0.040 | 4419.343 | 4350.644 | +03h 31m 18.99s | 52d49m44.8932s | +43d 53m 26.7s | 43d53m26.6676s |
13 | 3.344602 | GKPer | 30842 | 52.726982 | 43.922663 | 254.061770 | 2007-03-09T00:16:01 | 2007-03-09T23:26:23 | 18 | 00030842018 | 4570 | 4311.634 | 111.419 | 4200.215 | 4228.598 | +03h 30m 54.48s | 52d43m37.1352s | +43d 55m 21.6s | 43d55m21.5868s |
14 | 2.501833 | GKPer | 45767 | 52.743700 | 43.913840 | 249.208788 | 2013-03-20T01:02:59 | 2013-03-21T01:20:22 | 5 | 00045767005 | 4143 | 4121.740 | 63.538 | 4058.202 | 4087.774 | +03h 30m 58.49s | 52d44m37.32s | +43d 54m 49.8s | 43d54m49.824s |
15 | 1.642341 | GKPer | 30842 | 52.770293 | 43.921326 | 308.921791 | 2006-12-20T16:12:19 | 2006-12-20T18:34:48 | 1 | 00030842001 | 4022 | 3980.145 | 33.504 | 3946.641 | 3988.765 | +03h 31m 4.87s | 52d46m13.0548s | +43d 55m 16.8s | 43d55m16.7736s |
16 | 0.962148 | GKPer | 30842 | 52.778149 | 43.901410 | 269.888920 | 2007-02-04T14:50:01 | 2007-02-04T20:23:10 | 8 | 00030842008 | 4046 | 3979.563 | 43.122 | 3936.441 | 3978.030 | +03h 31m 6.76s | 52d46m41.3364s | +43d 54m 5.1s | 43d54m05.076s |
17 | 3.541167 | GKPer | 30842 | 52.718735 | 43.897146 | 242.945235 | 2007-03-02T12:04:01 | 2007-03-02T22:36:04 | 16 | 00030842016 | 3410 | 3261.669 | 118.117 | 3143.552 | 3288.439 | +03h 30m 52.50s | 52d43m07.446s | +43d 53m 49.7s | 43d53m49.7256s |
18 | 2.875683 | GKPer | 30842 | 52.734551 | 43.912719 | 261.255083 | 2007-02-23T00:06:01 | 2007-02-23T02:36:36 | 14 | 00030842014 | 3277 | 3244.444 | 5.008 | 3239.436 | 3243.491 | +03h 30m 56.29s | 52d44m04.3836s | +43d 54m 45.8s | 43d54m45.7884s |
19 | 1.589266 | GKPer | 30842 | 52.763626 | 43.900668 | 252.039706 | 2007-02-12T02:16:00 | 2007-02-12T14:23:37 | 11 | 00030842011 | 3159 | 3023.056 | 59.527 | 2963.529 | 3023.295 | +03h 31m 3.27s | 52d45m49.0536s | +43d 54m 2.4s | 43d54m02.4048s |
20 | 5.582058 | GKPer | 30842 | 52.878237 | 43.978359 | 50.625546 | 2007-09-27T14:04:00 | 2007-09-28T01:29:45 | 20 | 00030842020 | 3089 | 3010.216 | 0.000 | 3010.216 | 0.000 | +03h 31m 30.78s | 52d52m41.6532s | +43d 58m 42.1s | 43d58m42.0924s |
As you can see, this time, the results are ordered by the "xrt_exposure" column, in descending order. Incidentally, you don't have to actually retrieve the column you sort on, if you don't want to!
The default behaviour of this module is to get all rows in the database that match your query. This can be a lot, and maybe you don't want them all. You can limit how many are returned using the maxRows
variable:
q.unlock()
q.maxRows=3
q.submit()
Calling DB look-up for rows 0 -- 3
Resolved 'GK Per' as (52.80004119305, 43.90429720867) via SIMBAD
Received 3 rows.
This returned us the top three rows matching our query - and note that as we have (just a moment ago) said that our results should be ordered by descending XRT exposure, we should have got the three observations with the longest exposures.
q.results
_r | name | target_id | ra | decl | roll_angle | start_time | stop_time | obs_segment | obsid | bat_exposure | xrt_exposure | xrt_expo_wt | xrt_expo_pc | uvot_exposure | ra_s | ra_apy | decl_s | decl_apy | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2.940585 | GKPer | 30842 | 52.732920 | 43.912266 | 255.973292 | 2007-03-06T04:39:01 | 2007-03-06T14:14:01 | 17 | 00030842017 | 7951 | 7846.457 | 51.297 | 7795.160 | 7848.622 | +03h 30m 55.90s | 52d43m58.512s | +43d 54m 44.2s | 43d54m44.1576s |
1 | 2.802018 | GKPer | 30842 | 52.745146 | 43.929141 | 260.414911 | 2007-02-26T00:25:01 | 2007-02-26T06:08:24 | 15 | 00030842015 | 6488 | 6403.276 | 32.808 | 6370.468 | 6418.423 | +03h 30m 58.84s | 52d44m42.5256s | +43d 55m 44.9s | 43d55m44.9076s |
2 | 2.570384 | GKPer | 30842 | 52.740645 | 43.906271 | 262.071536 | 2007-02-16T02:39:01 | 2007-02-16T08:20:54 | 12 | 00030842012 | 6303 | 6238.041 | 82.605 | 6155.436 | 6237.197 | +03h 30m 57.75s | 52d44m26.322s | +43d 54m 22.6s | 43d54m22.5756s |
We did!
I said a moment ago that by default all matching rows are returned, and this is true. Some of you may be wondering why, therefore, the output above repeatedly says: "Calling DB look-up for rows 0 -- 1000" (if you are not wondering this, skip to the next section). Was I lying about getting all rows?
No, I wasn't, and in general what I'm about to say won't matter, but it's here to satisfy your curiosity.
There is a limit to the amount of server resources a single query can consume. In practice this means that if you request some enormous query with lots of rows, the query will be terminated because it uses more memory than is permitted and you will get some unhelpful error (probably an HTTP 500 error). To avoid this, the Python module will never ask for more than 1,000 rows at a time. But this doesn't mean that you only get 1,000 rows; it just means that the Python back end will get your results in chunks, requesting the first 1,000 rows and then (if necessary) the next 1,000 etc., giving (non-silent) output such as:
Calling DB look-up for rows 0 -- 1000 Calling DB look-up for rows 1001 -- 2000 Calling DB look-up for rows 2001 -- 3000
The results of these calls will be stitched together for you and this entire process would be completely invisible if we hadn't said silent=False
.
Having identified observations using a query, you may wish to download them. You can do this by calling the downloadObsData()
function. This is literally just a wrapper the function of the same name in the data
module and as already warned, I'm not going to redocument that here. Arguments are passed through to data.downloadObsData()
as **kwargs
, with just three exceptions. These exceptions apply to almost every data product function provided throughout the query
module, so I will refer back to this section a few times on subsequent page. These exceptions are:
silent
and verbose
; these are properties of your query object, and are set from them.results
table.subset
argument, which lets you specify a subset of the results
table for which you want data products.This last point we will return to in a moment
First, let's do a simple demonstration of this. I will deliberately execute a query that doesn't get too many rows.
q = uq.ObsQuery(silent=False)
q.addConeSearch(name='GRB 210205A', radius=300)
q.submit()
q.results
Resetting query details
Need to get the metadata to check the query is valid.
Calling DB look-up for rows 0 -- 1000
Resolved 'GRB 210205A' as (347.219333, 56.296528) via SIMBAD
Received 2 rows.
_r | name | target_id | ra | decl | roll_angle | start_time | stop_time | obs_segment | obsid | bat_exposure | xrt_exposure | xrt_expo_wt | xrt_expo_pc | uvot_exposure | ra_s | ra_apy | decl_s | decl_apy | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 84.722734 | Burst (347.221, 56.294) | 1030629 | 347.229229 | 56.319413 | 197.285029 | 2021-02-05T13:54:35 | 2021-02-05T22:16:56 | 1 | 01030629001 | 7906.000 | 7866.813 | 0.00 | 7866.813 | 7786.218 | +23h 08m 55.01s | 347d13m45.2244s | +56d 19m 9.9s | 56d19m09.8868s |
1 | 158.399615 | Burst (347.221, 56.294) | 1030629 | 347.274472 | 56.328161 | 207.713073 | 2021-02-05T10:53:19 | 2021-02-05T12:40:56 | 0 | 01030629000 | 4833.061 | 1725.085 | 5.02 | 1714.987 | 1686.866 | +23h 09m 5.87s | 347d16m28.0992s | +56d 19m 41.4s | 56d19m41.3796s |
As you can see, this gave us two rows, and we can save the observations simply enough.
q.downloadObsData(destDir='/tmp/APIDemo_download1',
instruments=('BAT', 'XRT'),
getTDRSS=True)
Making directory /tmp/APIDemo_download1
Downloading 2 datasets
Making directory /tmp/APIDemo_download1/01030629001
Making directory /tmp/APIDemo_download1/01030629001/bat/
Making directory /tmp/APIDemo_download1/01030629001/bat/hk/
Making directory /tmp/APIDemo_download1/01030629001/bat/masktag/
Making directory /tmp/APIDemo_download1/01030629001/bat/rate/
Making directory /tmp/APIDemo_download1/01030629001/bat/survey/
Making directory /tmp/APIDemo_download1/01030629001/xrt/
Making directory /tmp/APIDemo_download1/01030629001/xrt/event/
Making directory /tmp/APIDemo_download1/01030629001/xrt/hk/
Making directory /tmp/APIDemo_download1/01030629001/auxil/
Downloading 01030629001: 0%| | 0/46 [00:00<?, ?files/s]
Making directory /tmp/APIDemo_download1/01030629000
Making directory /tmp/APIDemo_download1/01030629000/bat/
Making directory /tmp/APIDemo_download1/01030629000/bat/event/
Making directory /tmp/APIDemo_download1/01030629000/bat/hk/
Making directory /tmp/APIDemo_download1/01030629000/bat/masktag/
Making directory /tmp/APIDemo_download1/01030629000/bat/products/
Making directory /tmp/APIDemo_download1/01030629000/bat/rate/
Making directory /tmp/APIDemo_download1/01030629000/bat/survey/
Making directory /tmp/APIDemo_download1/01030629000/xrt/
Making directory /tmp/APIDemo_download1/01030629000/xrt/event/
Making directory /tmp/APIDemo_download1/01030629000/xrt/hk/
Making directory /tmp/APIDemo_download1/01030629000/auxil/
Making directory /tmp/APIDemo_download1/01030629000/tdrss/
Downloading 01030629000: 0%| | 0/90 [00:00<?, ?files/s]
The arguments I supplied to downloadObsData()
are the standard arguments for data.downloadObsData()
which are documented here.
One thing that is worth noting is that, since downloadObsData()
uses the observation identifier to work out which data you are asking for, if your query did not retrieve the relevant column, this function would fail. If you are manually selecting columns and want to ensure you have the necessary column you can always look at the metadata (described above). If the table in question contains observation identifiers then the metadata will include "IsObsCol", which will be 1 for the relevant column.
After performing a query you may realise that you don't want to get data for all of the rows, but only some of them. You could repeat the query, of course, with extra filtering, but this would be a bit wasteful. The subset
parameter solves this; it exists for all functions for getting products via the query
module, and allows you to define a subset of rows for which products should be retrieved.
The argument itself takes a pandas Series
of bool
s, identifying the rows, which sounds complicated, but actually you just need to give it a pandas
filter expression. It is beyond the scope of this documentation to describe those in detail, but the examples below should give you a primer.
First, I'm going to do a cone search around GK Per, ordering by exposure (longest first)
q = uq.ObsQuery(silent=False)
q.addConeSearch(name='GK Per', radius=300)
q.sortCol = 'xrt_exposure'
q.sortDir='DESC'
q.submit()
q.results
Resetting query details
Need to get the metadata.
Calling DB look-up for rows 0 -- 1000
Resolved 'GK Per' as (52.80004119305, 43.90429720867) via SIMBAD
Received 145 rows.
_r | name | target_id | ra | decl | roll_angle | start_time | stop_time | obs_segment | obsid | bat_exposure | xrt_exposure | xrt_expo_wt | xrt_expo_pc | uvot_exposure | ra_s | ra_apy | decl_s | decl_apy | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 176.435074 | GKPer | 30842 | 52.732920 | 43.912266 | 255.973292 | 2007-03-06T04:39:01 | 2007-03-06T14:14:01 | 17 | 00030842017 | 7951 | 7846.457 | 51.297 | 7795.160 | 7848.622 | +03h 30m 55.90s | 52d43m58.512s | +43d 54m 44.2s | 43d54m44.1576s |
1 | 168.121066 | GKPer | 30842 | 52.745146 | 43.929141 | 260.414911 | 2007-02-26T00:25:01 | 2007-02-26T06:08:24 | 15 | 00030842015 | 6488 | 6403.276 | 32.808 | 6370.468 | 6418.423 | +03h 30m 58.84s | 52d44m42.5256s | +43d 55m 44.9s | 43d55m44.9076s |
2 | 154.223024 | GKPer | 30842 | 52.740645 | 43.906271 | 262.071536 | 2007-02-16T02:39:01 | 2007-02-16T08:20:54 | 12 | 00030842012 | 6303 | 6238.041 | 82.605 | 6155.436 | 6237.197 | +03h 30m 57.75s | 52d44m26.322s | +43d 54m 22.6s | 43d54m22.5756s |
3 | 25.319939 | GKPer | 30842 | 52.793185 | 43.899291 | 280.915675 | 2007-01-17T03:10:01 | 2007-01-17T13:41:49 | 5 | 00030842005 | 6053 | 6078.284 | 60.784 | 6017.500 | 5862.453 | +03h 31m 10.36s | 52d47m35.466s | +43d 53m 57.4s | 43d53m57.4476s |
4 | 156.411729 | GKPer | 30842 | 52.745736 | 43.923199 | 253.806039 | 2007-03-13T00:29:01 | 2007-03-13T11:01:02 | 19 | 00030842019 | 6106 | 5969.147 | 78.192 | 5890.955 | 5981.222 | +03h 30m 58.98s | 52d44m44.6496s | +43d 55m 23.5s | 43d55m23.5164s |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
140 | 105.896403 | GKPeroffset1 | 31653 | 52.809250 | 43.875640 | 239.062783 | 2010-03-22T22:23:00 | 2010-03-22T23:17:16 | 32 | 00031653032 | 0 | 42.338 | 4.109 | 38.229 | 42.565 | +03h 31m 14.22s | 52d48m33.3s | +43d 52m 32.3s | 43d52m32.304s |
141 | 17.510511 | GKPeroffset1 | 31653 | 52.793651 | 43.905866 | 239.071668 | 2010-03-12T11:49:59 | 2010-03-12T12:44:19 | 11 | 00031653011 | 301 | 39.563 | 1.963 | 37.600 | 39.575 | +03h 31m 10.48s | 52d47m37.1436s | +43d 54m 21.1s | 43d54m21.1176s |
142 | 116.995543 | GKPer | 30842 | 52.833519 | 43.926082 | 255.065851 | 2010-03-09T00:08:00 | 2010-03-09T01:01:34 | 26 | 00030842026 | 300 | 26.660 | 4.104 | 22.556 | 24.239 | +03h 31m 20.04s | 52d50m00.6684s | +43d 55m 33.9s | 43d55m33.8952s |
143 | 117.981049 | GKPer | 30842 | 52.836633 | 43.923770 | 255.063174 | 2010-03-09T21:01:00 | 2010-03-09T21:54:33 | 30 | 00030842030 | 300 | 26.239 | 13.712 | 12.527 | 25.243 | +03h 31m 20.79s | 52d50m11.8788s | +43d 55m 25.6s | 43d55m25.572s |
144 | 73.054432 | GKPeroffset1 | 31653 | 52.792369 | 43.923823 | 240.067049 | 2010-04-05T02:43:00 | 2010-04-05T19:44:47 | 48 | 00031653048 | 900 | 14.457 | 14.457 | 0.000 | 11.585 | +03h 31m 10.17s | 52d47m32.5284s | +43d 55m 25.8s | 43d55m25.7628s |
145 rows × 19 columns
Now, looking at this, I want to get only those rows that have more than 6ks of XRT data. That is, those rows where q.results['xrt_exposure']>6000
, and that is my subset definition:
q.downloadObsData(destDir='/tmp/APIDemo_download2',
subset=q.results['xrt_exposure']>6000,
instruments=('XRT',),
getAuxil=False)
Making directory /tmp/APIDemo_download2
Downloading 4 datasets
Making directory /tmp/APIDemo_download2/00030842017
Making directory /tmp/APIDemo_download2/00030842017/xrt/
Making directory /tmp/APIDemo_download2/00030842017/xrt/event/
Making directory /tmp/APIDemo_download2/00030842017/xrt/hk/
Making directory /tmp/APIDemo_download2/00030842017/xrt/ssin/
Downloading 00030842017: 0%| | 0/109 [00:00<?, ?files/s]
Making directory /tmp/APIDemo_download2/00030842015
Making directory /tmp/APIDemo_download2/00030842015/xrt/
Making directory /tmp/APIDemo_download2/00030842015/xrt/event/
Making directory /tmp/APIDemo_download2/00030842015/xrt/hk/
Making directory /tmp/APIDemo_download2/00030842015/xrt/ssin/
Downloading 00030842015: 0%| | 0/99 [00:00<?, ?files/s]
Making directory /tmp/APIDemo_download2/00030842012
Making directory /tmp/APIDemo_download2/00030842012/xrt/
Making directory /tmp/APIDemo_download2/00030842012/xrt/event/
Making directory /tmp/APIDemo_download2/00030842012/xrt/hk/
Making directory /tmp/APIDemo_download2/00030842012/xrt/ssin/
Downloading 00030842012: 0%| | 0/99 [00:00<?, ?files/s]
Making directory /tmp/APIDemo_download2/00030842005
Making directory /tmp/APIDemo_download2/00030842005/xrt/
Making directory /tmp/APIDemo_download2/00030842005/xrt/event/
Making directory /tmp/APIDemo_download2/00030842005/xrt/hk/
Making directory /tmp/APIDemo_download2/00030842005/xrt/ssin/
Downloading 00030842005: 0%| | 0/114 [00:00<?, ?files/s]
If you want to check your filter expression before actually using it, you can use the .loc
property of a DataFrame
like this:
q.results.loc[q.results['xrt_exposure']>6000]
_r | name | target_id | ra | decl | roll_angle | start_time | stop_time | obs_segment | obsid | bat_exposure | xrt_exposure | xrt_expo_wt | xrt_expo_pc | uvot_exposure | ra_s | ra_apy | decl_s | decl_apy | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 176.435074 | GKPer | 30842 | 52.732920 | 43.912266 | 255.973292 | 2007-03-06T04:39:01 | 2007-03-06T14:14:01 | 17 | 00030842017 | 7951 | 7846.457 | 51.297 | 7795.160 | 7848.622 | +03h 30m 55.90s | 52d43m58.512s | +43d 54m 44.2s | 43d54m44.1576s |
1 | 168.121066 | GKPer | 30842 | 52.745146 | 43.929141 | 260.414911 | 2007-02-26T00:25:01 | 2007-02-26T06:08:24 | 15 | 00030842015 | 6488 | 6403.276 | 32.808 | 6370.468 | 6418.423 | +03h 30m 58.84s | 52d44m42.5256s | +43d 55m 44.9s | 43d55m44.9076s |
2 | 154.223024 | GKPer | 30842 | 52.740645 | 43.906271 | 262.071536 | 2007-02-16T02:39:01 | 2007-02-16T08:20:54 | 12 | 00030842012 | 6303 | 6238.041 | 82.605 | 6155.436 | 6237.197 | +03h 30m 57.75s | 52d44m26.322s | +43d 54m 22.6s | 43d54m22.5756s |
3 | 25.319939 | GKPer | 30842 | 52.793185 | 43.899291 | 280.915675 | 2007-01-17T03:10:01 | 2007-01-17T13:41:49 | 5 | 00030842005 | 6053 | 6078.284 | 60.784 | 6017.500 | 5862.453 | +03h 31m 10.36s | 52d47m35.466s | +43d 53m 57.4s | 43d53m57.4476s |
And you can see that the DataFrame
we got back contains only the rows we wanted.
I'll give a few more examples of creating subsets, and rather then fill up your /tmp
area by downloading, I'll define them as variables and print them using .loc
, but you can just put my examples straight into the subset
argument of downloadObsData
. I'll include such code but commented out, below.
First, let's show you how to apply multiple filters because this always take me 3 attempts to get right. Let's say we only want results with more than 6ks of XRT exposure, but less than 7ks.
subset = (q.results['xrt_exposure']>6000)&(q.results['xrt_exposure']<7000)
q.results.loc[subset]
# Uncomment the following if you want:
# q.downloadObsData(destDir='/tmp/APIDemo_download3',
# subset=(q.results['xrt_exposure']>6000)&(q.results['xrt_exposure']<7000),
# instruments=('XRT',),
# getAuxil=False)
_r | name | target_id | ra | decl | roll_angle | start_time | stop_time | obs_segment | obsid | bat_exposure | xrt_exposure | xrt_expo_wt | xrt_expo_pc | uvot_exposure | ra_s | ra_apy | decl_s | decl_apy | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 168.121066 | GKPer | 30842 | 52.745146 | 43.929141 | 260.414911 | 2007-02-26T00:25:01 | 2007-02-26T06:08:24 | 15 | 00030842015 | 6488 | 6403.276 | 32.808 | 6370.468 | 6418.423 | +03h 30m 58.84s | 52d44m42.5256s | +43d 55m 44.9s | 43d55m44.9076s |
2 | 154.223024 | GKPer | 30842 | 52.740645 | 43.906271 | 262.071536 | 2007-02-16T02:39:01 | 2007-02-16T08:20:54 | 12 | 00030842012 | 6303 | 6238.041 | 82.605 | 6155.436 | 6237.197 | +03h 30m 57.75s | 52d44m26.322s | +43d 54m 22.6s | 43d54m22.5756s |
3 | 25.319939 | GKPer | 30842 | 52.793185 | 43.899291 | 280.915675 | 2007-01-17T03:10:01 | 2007-01-17T13:41:49 | 5 | 00030842005 | 6053 | 6078.284 | 60.784 | 6017.500 | 5862.453 | +03h 31m 10.36s | 52d47m35.466s | +43d 53m 57.4s | 43d53m57.4476s |
And lastly, the isin
function which is really handy as well. This lets us make a subset by giving some values we want a column to contain.So, for example, imagine I only wanted to download the data in the above for those cases with a "target_id" of 81445, 45767 or 81637. I could do that as follows:
myTargs = (81445, 45767, 81637)
subset=q.results['target_id'].isin(myTargs)
q.results.loc[q.results['target_id'].isin(myTargs)]
# q.downloadObsData(destDir='/tmp/APIDemo_download4',
# subset=q.results['target_id'].isin(myTargs),
# instruments=('XRT',),
# getAuxil=False)
_r | name | target_id | ra | decl | roll_angle | start_time | stop_time | obs_segment | obsid | bat_exposure | xrt_exposure | xrt_expo_wt | xrt_expo_pc | uvot_exposure | ra_s | ra_apy | decl_s | decl_apy | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
12 | 89.880318 | GK_Per | 81445 | 52.829137 | 43.890741 | 63.483317 | 2015-09-09T09:14:58 | 2015-09-09T18:04:10 | 2 | 00081445002 | 4458 | 4419.383 | 0.040 | 4419.343 | 4350.644 | +03h 31m 18.99s | 52d49m44.8932s | +43d 53m 26.7s | 43d53m26.6676s |
14 | 150.109990 | GKPer | 45767 | 52.743700 | 43.913840 | 249.208788 | 2013-03-20T01:02:59 | 2013-03-21T01:20:22 | 5 | 00045767005 | 4143 | 4121.740 | 63.538 | 4058.202 | 4087.774 | +03h 30m 58.49s | 52d44m37.32s | +43d 54m 49.8s | 43d54m49.824s |
23 | 34.471666 | GK_Per | 81637 | 52.812729 | 43.901448 | 59.655334 | 2015-09-08T20:24:58 | 2015-09-08T22:15:56 | 1 | 00081637001 | 2047 | 2336.794 | 0.000 | 2336.794 | 2317.685 | +03h 31m 15.05s | 52d48m45.8244s | +43d 54m 5.2s | 43d54m05.2128s |
25 | 35.612521 | GKPer | 45767 | 52.787061 | 43.901074 | 87.297491 | 2012-07-07T06:29:59 | 2012-07-07T21:44:45 | 2 | 00045767002 | 0 | 2056.139 | 0.169 | 2055.970 | 2046.231 | +03h 31m 8.89s | 52d47m13.4196s | +43d 54m 3.9s | 43d54m03.8664s |
34 | 117.912554 | GK_Per | 81445 | 52.754590 | 43.904928 | 243.739529 | 2015-03-31T19:32:59 | 2015-03-31T21:43:07 | 1 | 00081445001 | 1899 | 1894.012 | 1894.012 | 0.000 | 1859.323 | +03h 31m 1.10s | 52d45m16.524s | +43d 54m 17.7s | 43d54m17.7408s |
88 | 30.286534 | GKPer | 45767 | 52.793330 | 43.897413 | 84.556936 | 2012-07-10T00:12:59 | 2012-07-10T21:55:42 | 4 | 00045767004 | 240 | 992.861 | 0.000 | 992.861 | 960.151 | +03h 31m 10.40s | 52d47m35.988s | +43d 53m 50.7s | 43d53m50.6868s |
94 | 43.622898 | GKPer | 45767 | 52.802583 | 43.892319 | 87.346896 | 2012-07-09T01:47:59 | 2012-07-09T02:53:33 | 3 | 00045767003 | 0 | 977.932 | 0.093 | 977.839 | 966.009 | +03h 31m 12.62s | 52d48m09.2988s | +43d 53m 32.3s | 43d53m32.3484s |
Astute readers will have realised that this effectively takes place of the IN
operator in SQL, which is not supported by the addFilter
command (largely for security reasons).