Quick Start

This brief tutorial provides a step-by-step guide to using PyRCS, highlighting its key functionalities. It demonstrates how to retrieve three key categories of codes used in the UK railway system, which are commonly applied in both practical and research contexts:

Through practical examples, this tutorial will guide you in understanding how PyRCS works and how to use it effectively.

Location Identifiers

< Back to Top | Next >

The location identifiers, including CRS, NLC, TIPLOC and STANOX codes, are classified as line data on the Railway Codes website. To retrieve these codes using PyRCS, we can use the LocationIdentifiers class, contained in the line_data subpackage.

First, let’s import the class and create an instance:

>>> from pyrcs.line_data import LocationIdentifiers
>>> # Alternatively, from pyrcs import LocationIdentifiers

>>> lid = LocationIdentifiers()

>>> lid.NAME
'CRS, NLC, TIPLOC and STANOX codes'

>>> lid.URL
'http://www.railwaycodes.org.uk/crs/crs0.shtm'

Alternatively, we can create the instance using the LineData class:

>>> from pyrcs.collector import LineData
>>> # Alternatively, from pyrcs import LineData

>>> ld = LineData()
>>> lid_ = ld.LocationIdentifiers

>>> lid.NAME == lid_.NAME
True

Note

  • The instance ld encompasses all classes within the line data category.

  • lid_ is equivalent to lid.

Location identifiers by initial letter

We can retrieve codes (in pandas.DataFrame format) for all locations starting with a specific letter using the LocationIdentifiers.collect_loc_id() method. This input value for the parameter is case-insensitive. For example, to get the codes for locations whose names begin with the letter 'A' (or 'a'):

>>> loc_a_codes = lid.collect_loc_id(initial='a', verbose=True)
Proceed with collecting data of "CRS, NLC, TIPLOC and STANOX codes" beginning with "A"?
 [No]|Yes: yes
Collecting the data ... Done.

>>> type(loc_a_codes)
dict

>>> list(loc_a_codes)
['A', 'Notes', 'Last updated date']

As shown above, loc_a_codes is a dictionary (i.e. in dict format) with the following keys:

  • 'A'

  • 'Notes'

  • 'Last updated date'

The corresponding values are:

  • loc_a_codes['A'] - CRS, NLC, TIPLOC and STANOX codes for the locations whose names begin with 'A', referring to the table on the Locations beginning A web page.

  • loc_a_codes['Notes'] - Any additional information provided on the web page (if available).

  • loc_a_codes['Last updated date'] - The date when the Locations beginning A web page was last updated.

A snapshot of the data contained in loc_a_codes is demonstrated below:

>>> loc_a_codes_dat = loc_a_codes['A']

>>> type(loc_a_codes_dat)
pandas.DataFrame

>>> loc_a_codes_dat
                                 Location CRS  ... STANME_Note STANOX_Note
0                    1999 Reorganisations      ...
1                                      A1      ...
2                          A463 Traded In      ...
3     A483 Road Scheme Supervisors Closed      ...
4                                  Aachen      ...
...                                   ...  ..  ...         ...         ...
3405                       Ayr Wagon Team      ...
3406                       Ayr Wagon Team      ...
3407                       Ayr Wagon Team      ...
3408                          Ayr Welders      ...
3409                    Aztec Travel S378      ...
[3410 rows x 12 columns]

>>> print(f"Notes: {loc_a_codes['Notes']}")
>>> print(f"Last updated date: {loc_a_codes['Last updated date']}")
Notes: None
Last updated date: 2026-07-07

>>> ## Try more examples! Uncomment the lines below and run:
>>> # loc_a_codes = lid.fetch_loc_id('a')  # Fetch location codes starting with 'A'
>>> # loc_codes = lid.fetch_loc_id()  # Fetch all location codes

All available location identifiers

Beyond retrieving location codes for a specific letter, we can use the LocationIdentifiers.fetch_codes() method to obtain codes for all locations with names starting from 'A' to 'Z':

>>> loc_codes = lid.fetch_codes()

>>> type(loc_codes)
dict
>>> list(loc_codes)
['Location ID', 'Other systems', 'Notes', 'Last updated date']

The loc_codes object is a dictionary with the following keys:

  • 'Location ID'

  • 'Other systems'

  • 'Notes'

  • 'Latest update date'

The corresponding values are:

  • loc_codes['Location ID'] - CRS, NLC, TIPLOC, and STANOX codes for all locations listed across the relevant web pages.

  • loc_codes['Other systems'] - Codes related to the other systems.

  • loc_codes['Notes'] - Any notes and information (if available).

  • loc_codes['Latest update date'] - The latest 'Last updated date' across all initial-specific data.

Here is a snapshot of the data contained in loc_codes:

>>> lid.KEY
'Location ID'

>>> loc_codes_dat = loc_codes[lid.KEY]  # loc_codes['Location ID']

>>> type(loc_codes_dat)
pandas.DataFrame

>>> loc_codes_dat
                                  Location CRS  ... STANME_Note STANOX_Note
0                     1999 Reorganisations      ...
1                                       A1      ...
2                           A463 Traded In      ...
3      A483 Road Scheme Supervisors Closed      ...
4                                   Aachen      ...
...                                    ...  ..  ...         ...         ...
61916                              ZZTYALS      ...
61917                              ZZTYKKH      ...
61918                              ZZTYLIN      ...
61919                              ZZTYSGY      ...
61920                              ZZWMNST      ...
[61921 rows x 12 columns]

>>> loc_codes_dat[['Location', 'Location_Note']]
                                  Location    Location_Note
0                     1999 Reorganisations
1                                       A1
2                           A463 Traded In
3      A483 Road Scheme Supervisors Closed
4                                   Aachen
...                                    ...              ...
61916                              ZZTYALS       see Alston
61917                              ZZTYKKH    see Kirkhaugh
61918                              ZZTYLIN      see Lintley
61919                              ZZTYSGY   see Slaggyford
61920                              ZZWMNST  see Westminster
[61921 rows x 2 columns]

To access codes from other systems, such as Crossrail or the Tyne & Wear Metro:

>>> lid.KEY_TO_OTHER_SYSTEMS
'Other systems'

>>> os_codes_dat = loc_codes[lid.KEY_TO_OTHER_SYSTEMS]

>>> type(os_codes_dat)
dict
>>> list(os_codes_dat)
['Córas Iompair Éireann (Republic of Ireland)',
 'Crossrail',
 'Croydon Tramlink',
 'Docklands Light Railway',
 'Manchester Metrolink',
 'Translink (Northern Ireland)',
 'Tyne & Wear Metro']

For example, to view the data for Crossrail:

>>> crossrail_codes_dat = os_codes_dat['Crossrail']

>>> type(crossrail_codes_dat)
pandas.DataFrame

>>> crossrail_codes_dat
                                            Location  ... New operating code
0                                         Abbey Wood  ...                ABW
1        Abbey Wood Bolthole Berth/Crossrail Sidings  ...
2                                 Abbey Wood Sidings  ...
3                                        Bond Street  ...                BDS
4                                       Canary Wharf  ...                CWX
...                                              ...  ...                ...
26                                       Whitechapel  ...                ZLW
27               Whitechapel Vallance Road Crossover  ...
28                                          Woolwich  ...                WWC
29                                         [unknown]  ...
30                                         [unknown]  ...
[31 rows x 5 columns]

>>> ## Try more examples! Uncomment the lines below and run:
>>> ## Get a dictionary for STANOX codes and location names
>>> # stanox_dict = lid.make_xref_dict('STANOX')
>>> ## ... and for STANOX, TIPLOC and location names starting with 'A'
>>> # stanox_tiploc_dict_a = lid.make_xref_dict(['STANOX', 'TIPLOC'], initials='a')

ELRs and mileages

< Previous | Back to Top | Next >

Engineer’s Line References (ELRs) are also commonly encountered in various data sets within the UK’s railway system. To retrieve the codes for ELRs along with their associated mileage files, we can use the ELRMileages class:

>>> from pyrcs.line_data import ELRMileages
>>> # Alternatively, from pyrcs import ELRMileages

>>> em = ELRMileages()

>>> em.NAME
"Engineer's Line References (ELRs)"

>>> em.URL
'http://www.railwaycodes.org.uk/elrs/elr0.shtm'

Engineer’s Line References (ELRs)

Similar to location identifiers, the ELR codes on the Railway Codes website are arranged alphabetically based on their initial letters. We can use the ELRMileages.collect_elr() method to obtain ELRs starting with a specific letter. For example, to get the data for ELRs beginning with the letter 'A':

>>> elrs_a_codes = em.collect_elr(initial='a', verbose=True)
Proceed with collecting data of "Engineer's Line References (ELRs)" beginning with "A"?
 [No]|Yes: yes
Collecting the data ... Done.

>>> type(elrs_a_codes)
dict

>>> list(elrs_a_codes)
['A', 'Last updated date']

The elrs_a_codes object is a dictionary with the following keys:

  • 'A'

  • 'Last updated date'

The corresponding values are:

  • elrs_a_codes['A'] - Data for ELRs that begin with 'A', referring to the table presented on the ELRs beginning with A web page.

  • elrs_a_codes['Last updated date'] - The date when the ELRs beginning with A web page was last updated.

Here is a snapshot of the data contained in elrs_a_codes:

>>> elrs_a_codes_dat = elrs_a_codes['A']

>>> type(elrs_a_codes_dat)
pandas.DataFrame

>>> elrs_a_codes_dat
      ELR  ...         Notes
0     AAL  ...      Now NAJ3
1     AAM  ...  Formerly AML
2     AAV  ...
3     ABB  ...       Now AHB
4     ABB  ...
..    ...  ...           ...
188  AYR4  ...
189  AYR5  ...
190  AYR6  ...
191   AYS  ...
192   AYT  ...
[193 rows x 5 columns]

>>> print(f"Last updated date: {elrs_a_codes['Last updated date']}")
Last updated date: 2025-06-23

To retrieve data for all ELRs (from 'A' to 'Z'), we can use the ELRMileages.fetch_elr() method:

>>> elrs_codes = em.fetch_elr()

>>> type(elrs_codes)
dict

>>> list(elrs_codes)
['ELRs and mileages', 'Last updated date']

Similarly, elrs_codes is a dictionary with the following keys:

  • 'ELRs and mileages'

  • 'Latest update date'

The corresponding values are:

  • elrs_codes['ELRs and mileages'] - Codes for all available ELRs (with the initial letters ranging from 'A' to 'Z').

  • elrs_codes['Latest update date'] - The most recent update date among all the ELR data.

Here is a snapshot of the data contained in elrs_codes:

>>> elrs_codes_dat = elrs_codes[em.KEY]

>>> type(elrs_codes_dat)
pandas.DataFrame

>>> elrs_codes_dat
       ELR  ...         Notes
0      AAL  ...      Now NAJ3
1      AAM  ...  Formerly AML
2      AAV  ...
3      ABB  ...       Now AHB
4      ABB  ...
...    ...  ...           ...
4574  ZGW1  ...
4575  ZGW2  ...
4576   ZZY  ...
4577   ZZZ  ...
4578  ZZZ9  ...
[4579 rows x 5 columns]

>>> ## Try more examples! Uncomment the lines below and run:
>>> # elrs_a_codes = em.fetch_elr(initial='a')  # Fetch ELRs starting with 'A'
>>> # elrs_b_codes = em.fetch_elr(initial='B')  # Fetch ELRs starting with 'B'

Mileage file of a given ELR

In addition to the codes of ELRs, each ELR is associated with a mileage file that specifies the major mileages along the line. To retrieve this data, we can use the ELRMileages.fetch_mileage_file() method.

For example, to get the mileage file for ‘AAM’:

>>> amm_mileage_file = em.fetch_mileage_file(elr='AAM')

>>> type(amm_mileage_file)
dict

>>> list(amm_mileage_file)
['ELR', 'Line', 'Sub-Line', 'Mileage', 'Notes']

The amm_mileage_file object is also a dictionary and has the following keys:

  • 'ELR'

  • 'Line'

  • 'Sub-Line'

  • 'Mileage'

  • 'Notes'

The corresponding values are:

  • amm_mileage_file['ELR'] - The given ELR (in this example, 'AAM').

  • amm_mileage_file['Line'] - The name of the line associated with the ELR.

  • amm_mileage_file['Sub-Line'] - The sub-line name (if applicable).

  • amm_mileage_file['Mileage'] - The major mileages along the line.

  • amm_mileage_file['Notes'] - Additional notes or information (if available).

Here is a snapshot of the data contained in amm_mileage_file:

>>> amm_mileage_file['Line']
'Ashchurch and Malvern Line'

>>> amm_mileage_file['Mileage']
    Mileage Mileage_Note  ... Link_2_ELR Link_2_Mile_Chain
0    0.0000               ...
1    0.0154               ...
2    0.0396               ...
3    1.1012               ...
4    1.1408               ...
5    2.0154               ...
6    2.0308               ...
7    5.0330               ...
8    6.0396               ...
9    6.0528               ...
10   6.1078               ...
11   7.0374               ...
12  11.1298               ...
13  13.0638               ...
[14 rows x 11 columns]

>>> ## Try more examples! Uncomment the lines below and run:
>>> # xre_mileage_file = em.fetch_mileage_file('XRE')  # Fetch mileage file for 'XRE'
>>> # your_mileage_file = em.fetch_mileage_file(elr='?')  # ... and for a given ELR '?'

Railway station data

< Previous | Back to Top | Next >

The railway station data includes information such as the station name, ELR, mileage, status, owner, operator, coordinates and grid reference. This data is available in the other assets section of the Railway Codes website and can be retrieved using the Stations class contained in the other_assets subpackage.

To get the data, let’s import the Stations class and create an instance:

>>> from pyrcs.other_assets import Stations  # from pyrcs import Stations

>>> stn = Stations()

>>> stn.NAME
'Railway station data'

>>> stn.URL
'http://www.railwaycodes.org.uk/stations/station0.shtm'

Alternatively, we can also create the instance by using the OtherAssets class:.

>>> from pyrcs.collector import OtherAssets  # from pyrcs import OtherAssets

>>> oa = OtherAssets()
>>> stn_ = oa.Stations

>>> stn.NAME == stn_.NAME
True

Note

  • The instance stn encompasses all classes within the other assets category.

  • stn_ is equivalent to stn.

Railway stations by initial letter

We can obtain railway station data based on the first letter (e.g. 'A' or 'Z') of the station’s name using the Stations.collect_locations() method. For example, to get data for stations starting with 'A':

>>> stn_loc_a_codes = stn.collect_locations(initial='a', verbose=True)
Proceed with collecting data of "mileages, operators and grid coordinates" beginning with "A"?
 [No]|Yes: yes
Collecting the data ... Done.

>>> type(stn_loc_a_codes)
dict

>>> list(stn_loc_a_codes)
['A', 'Last updated date']

The dictionary stn_loc_a_codes includes the following keys:

  • 'A'

  • 'Last updated date'

The corresponding values are:

  • stn_loc_a_codes['A'] - Data for railway stations whose names begin with 'A', including mileages, operators and grid coordinates, referring to the table on the Stations beginning with A web page.

  • stn_loc_a_codes['Last updated date'] - The date when the Stations beginning with A web page was last updated.

Here is a snapshot of the data contained in stn_loc_a:

>>> stn_loc_a_codes_dat = stn_loc_a_codes['A']

>>> type(stn_loc_a_codes_dat)
pandas.DataFrame

>>> stn_loc_a_codes_dat
                    Station  ... CRS Note
0                Abbey Wood  ...
1                Abbey Wood  ...
2                      Aber  ...
3                 Abercynon  ...
4                 Abercynon  ...
..                      ...  ...      ...
138  Aylesbury Vale Parkway  ...
139               Aylesford  ...
140                Aylesham  ...
141                     Ayr  ...
142                     Ayr  ...
[143 rows x 15 columns]

>>> stn_loc_a_codes_dat.columns.to_list()
['Station',
 'Station Note',
 'Picture Link',
 'ELR',
 'Mileage',
 'Note',
 'Owner',
 'Former Owner',
 'Operator',
 'Former Operator',
 'Degrees Longitude',
 'Degrees Latitude',
 'Grid Reference',
 'CRS',
 'CRS Note']

>>> stn_loc_a_codes_dat[['Station', 'ELR', 'Mileage']]
                    Station   ELR   Mileage
0                Abbey Wood   NKL  11m 43ch
1                Abbey Wood   XRS  24.458km
2                      Aber   CAR   8m 69ch
3                 Abercynon   CAM  16m 28ch
4                 Abercynon   ABD  16m 28ch
..                      ...   ...       ...
138  Aylesbury Vale Parkway  MCJ2  40m 38ch
139               Aylesford  PWS2  38m 74ch
140                Aylesham   FDM  68m 66ch
141                     Ayr  AYR6  40m 49ch
142                     Ayr  STR1  40m 49ch
[143 rows x 3 columns]

>>> print(f"Last updated date: {stn_loc_a_codes['Last updated date']}")
Last updated date: 2026-06-16

All available railway stations

To retrieve data for all railway stations available in the other assets category, we can use the Stations.fetch_locations() method:

>>> stn_loc_codes = stn.fetch_locations()

>>> type(stn_loc_codes)
dict

>>> list(stn_loc_codes)
['Mileages, operators and grid coordinates', 'Last updated date']

The dictionary stn_loc_codes includes the following keys:

  • 'Mileages, operators and grid coordinates'

  • 'Latest update date'

The corresponding values are:

  • stn_loc_codes['Mileages, operators and grid coordinates'] - Data for all railway stations, with the initial letters ranging from 'A' to 'Z'.

  • stn_loc_codes['Latest update date'] - The most recent update date among all the station data.

Here is a snapshot of the data contained in stn_loc_codes:

>>> stn.KEY_TO_STN
'Mileages, operators and grid coordinates'

>>> stn_loc_codes_dat = stn_loc_codes[stn.KEY_TO_STN]

>>> type(stn_loc_codes_dat)
pandas.DataFrame

>>> stn_loc_codes_dat
             Station                               Station Note  ...  CRS CRS Note
0         Abbey Wood                                             ...  ABW
1         Abbey Wood                                             ...  ABW
2               Aber                                             ...  ABE
3          Abercynon  formerly 'Abercynon South' to 24 May 2008  ...  ACY
4          Abercynon  formerly 'Abercynon South' to 24 May 2008  ...  ACY
...              ...                                        ...  ...  ...      ...
2927            York                                             ...  YRK
2928          Yorton                                             ...  YRT
2929   Ystrad Mynach                                             ...  YSM
2930   Ystrad Mynach                                             ...  YSM
2931  Ystrad Rhondda                                             ...  YSR
[2932 rows x 15 columns]

>>> loc_cols = ['Station', 'ELR', 'Mileage', 'Degrees Longitude', 'Degrees Latitude']
>>> stn_loc_codes_dat[loc_cols].head()
      Station  ELR   Mileage  Degrees Longitude  Degrees Latitude
0  Abbey Wood  NKL  11m 43ch             0.1204           51.4908
1  Abbey Wood  XRS  24.458km             0.1204           51.4908
2        Aber  CAR   8m 69ch            -3.2305           51.5755
3   Abercynon  CAM  16m 28ch            -3.3294           51.6434
4   Abercynon  ABD  16m 28ch            -3.3294           51.6434

>>> print(f"Last updated date: {stn_loc_codes['Last updated date']}")
Last updated date: 2026-08-19

>>> ## Try more examples! Uncomment the lines below and run:
>>> # stn_loc_a_codes = em.fetch_locations('a')  # railway stations starting with 'A'
>>> # your_stn_loc_codes = em.fetch_locations('?')  # ... and a given letter '?'

< Previous | Back to Top


Any issues regarding the use of pyrcs are welcome and can be logged/reported onto the Issue Tracker.

For more details and examples, check Subpackages and Modules.