The text file ex6-2-b-mountain-data.txt, reproduced below, gives some data concerning the 8000 m peaks, in alphabetical order.
ex6-2-b-mountain-data.txt
This file contains a list of the 14 highest mountains in the
world with their names, height, year of first ascent, year of
first winter ascent, and location as longitude and latitude
in degrees (d), minutes (m) and seconds (s).
Note: as of 2019, no winter ascent has been made of K2.
--------------------------------------------------------------------
Name Height First Ascent First Winter Location
m Date Ascent Date (WGS84)
--------------------------------------------------------------------
Annapurna I 8091 3/6/1950 3/2/1987 28d35m46sN 83d49m13sE
Broad Peak 8051 9/6/1957 5/3/2013 35d48m39sN 76d34m06sE
Cho Oyu 8201 19/10/1954 12/2/1985 28d05m39sN 86d39m39sE
Dhaulagiri I 8167 13/5/1960 21/1/1985 27d59m17sN 86d55m31sE
Everest 8848 29/5/1953 17/2/1980 27d59m17sN 86d55m31sE
Gasherbrum I 8080 5/7/1958 9/3/2012 35d43m28sN 76d41m47sE
Gasherbrum II 8034 7/7/1956 2/2/2011 35d45m30sN 76d39m12sE
K2 8611 31/7/1954 - 35d52m57sN 76d30m48sE
Kangchenjunga 8568 25/5/1955 11/1/1986 27d42m09sN 88d08m54sE
Lhotse 8516 18/5/1956 31/12/1988 27d57m42sN 86d56m00sE
Makalu 8485 15/5/1955 9/2/2009 27d53m21sN 87d05m19sE
Manaslu 8163 9/5/1956 12/1/1984 28d33m0sN 84d33m35sE
Nanga Parbat 8126 3/7/1953 16/2/2016 35d14m15sN 74d35m21sE
Shishapangma 8027 2/5/1964 14/1/2005 28d21m8sN 85d46m47sE
--------------------------------------------------------------------
Note: the first winter ascent of K2 was made by a team of Nepali climbers in January 2021, but not included in the above data set.
Use NumPy's genfromtxt
method to read these data into a suitable structured array in order to determine the following:
(a) The lowest 8000 m peak
(b) The most northely, easterly, southerly and westerly peaks
(c) The most recent first ascent of the peaks
(d) The first of the peaks to be climbed in winter
Also produce another structured array containing a list of mountains with their height in feet (1 metre = 3.2808399 feet) and first ascent date, ordered by increasing height.
Reading this text file with numpy.genfromtxt
will require converter functions to handle the dates and locations (longitudes and latitudes). We will choose to store the former as a float
: in this solution, we will convert dates into the number of seconds since the midnight on 1 January 1970 (the "Unix epoch"), although any fiducial date will do. Note that this means that dates before this time will be represented by negative numbers.
The locations we will convert into float
values in decimal degrees. Latitudes will be positive values from 0 to 90 in the Northern hemisphere and negative values from 0 to -90 in the Southern hemisphere; longitudes will be from 0 to 180 in the Eastern hemisphere and from 0 to -180 in the Western hemisphere.
The code is as follows:
import numpy as np
from datetime import datetime, timedelta
EPOCH = datetime(1970, 1, 1)
dt = np.dtype({'names': ['name', 'height', 'first_ascent',
'first_winter_ascent', 'latitude', 'longitude'],
'formats': ['|S13', 'u2', 'f8', 'f8', 'f8', 'f8']
})
def angle_str_to_float(s):
"""
Convert an angle given in degrees, D, minutes, M, and seconds, S as a
string 'DdMdSs' in to a float representing decimal degrees.
"""
d, rem = s.split('d')
m, rem = rem.split('m')
s, rem = rem.split('s')
return float(d) + float(m)/60 + float(s)/3600
def angle_float_to_str(abs_angle):
"""
Convert a float representing an angle in degrees to a string of the form
'DdMdSs' representing degrees, D, minutes, M, and seconds, S.
"""
d = int(abs_angle)
fmins = (abs_angle - d) * 60
m = int(fmins)
s = int(round((fmins - m) * 60))
return '{D:d}d{M:d}m{S:d}s'.format(D=d, M=m, S=s)
def longlat_str_to_float(s):
"""
Convert a longitude or latitude given as a string 'DdMmSsZ' (Z=N, E, S or W)
into a float representing decimal degrees.
"""
s = s.decode('utf-8')
sgn = {'N': 1, 'E': 1, 'S': -1, 'W': -1}
return sgn[s[-1]] * angle_str_to_float(s[:-1])
def latitude_float_to_str(angle):
"""
Convert a latitude as a float between -90 and 90 degrees into a string
giving degrees, minutes and seconds: 'DdMmSsZ' (Z=N or S).
"""
hemisphere = 'N' if angle >=0 else 'S'
return angle_float_to_str(abs(angle)) + hemisphere
def longitude_float_to_str(angle):
"""
Convert a longitude as a float between -180 and 180 degrees into a string
giving degrees, minutes and seconds: 'DdMmSsZ' (Z=E or W).
"""
hemisphere = 'E' if angle >=0 else 'W'
return angle_float_to_str(abs(angle)) + hemisphere
def date_str_to_float(s):
"""
Converts a date represented as string formatted 'DD/MM/YYYY' to a number
of seconds since "the epoch" (1/1/1970).
"""
return (datetime.strptime(s.decode('utf-8'), '%d/%m/%Y') -
EPOCH).total_seconds()
def date_float_to_str(t):
"""
Convert a time in seconds since the epoch to a string formatted
'DD/MM/YYYY'
"""
return datetime.strftime((EPOCH + timedelta(seconds=t)), '%d/%m/%Y')
Using these functions, we can parse the file into a NumPy structured array and sort it for the required quantities:
peaks = np.genfromtxt('ex6-2-b-mountain-data.txt', dtype=dt, skip_header=11,
converters={2: date_str_to_float,
3: date_str_to_float,
4: longlat_str_to_float,
5: longlat_str_to_float},
delimiter=(13,5,14,12,13,11), skip_footer=1, autostrip=True)
ilowest = np.argmin(peaks['height'])
print('The lowest 8000 m peak is {:s} at {:4d} m'.format(
peaks[ilowest]['name'].decode('utf-8'), peaks[ilowest]['height']))
peaks.sort(order='latitude')
print('The most Northerly peak is {:s} at {:s} deg North'.format(
peaks[-1]['name'].decode('utf-8'),
latitude_float_to_str(peaks[-1]['latitude'])))
print('The most Southerly peak is {:s} at {:s} deg North'.format(
peaks[0]['name'].decode('utf-8'),
latitude_float_to_str(peaks[0]['latitude'])))
peaks.sort(order='longitude')
print('The most Easterly peak is {:s} at {:s} deg East'.format(
peaks[-1]['name'].decode('utf-8'),
longitude_float_to_str(peaks[-1]['longitude'])))
print('The most Westerly peak is {:s} at {:s} deg East'.format(
peaks[0]['name'].decode('utf-8'),
longitude_float_to_str(peaks[0]['longitude'])))
peaks.sort(order='first_ascent')
print('Most recent first ascent: {:s} on {:s}'.format(
peaks[-1]['name'].decode('utf-8'),
date_float_to_str(peaks[-1]['first_ascent'])))
i = np.nanargmax(peaks['first_winter_ascent'])
print('Most recent first winter ascent: {:s} on {:s}'.format(
peaks[i]['name'].decode('utf-8'),
date_float_to_str(peaks[i]['first_winter_ascent'])))
dt2 = np.dtype({'names': ['name', 'height', 'first_ascent'],
'formats': ['|S13', 'u2', '|S10']})
ordered_peaks = np.zeros(len(peaks), dtype=dt2)
ordered_peaks['name'] = peaks['name']
ordered_peaks['height'] = peaks['height'] * 3.2808399
ordered_peaks['first_ascent'] = [date_float_to_str(t)
for t in peaks['first_ascent']]
ordered_peaks = np.sort(ordered_peaks, order='height')[::-1]
print('+---------------+------------+--------------+')
print('| Peak | height /ft | first ascent |')
print('+---------------+------------+--------------+')
for peak, height, first_ascent in ordered_peaks:
print('| {:13s} | {:^10d} | {:^12s} |'.format(
peak.decode('utf-8'), height, first_ascent.decode('utf-8')))
print('+---------------+------------+--------------+')
The output is:
The lowest 8000 m peak is Shishapangma at 8027 m
The most Northerly peak is K2 at 35d52m57sN deg North
The most Southerly peak is Kangchenjunga at 27d42m9sN deg North
The most Easterly peak is Kangchenjunga at 88d8m54sE deg East
The most Westerly peak is Nanga Parbat at 74d35m21sE deg East
Most recent first ascent: Shishapangma on 02/05/1964
Most recent first winter ascent: Nanga Parbat on 16/02/2016
+---------------+------------+--------------+
| Peak | height /ft | first ascent |
+---------------+------------+--------------+
| Everest | 29028 | 29/05/1953 |
| K2 | 28251 | 31/07/1954 |
| Kangchenjunga | 28110 | 25/05/1955 |
| Lhotse | 27939 | 18/05/1956 |
| Makalu | 27837 | 15/05/1955 |
| Cho Oyu | 26906 | 19/10/1954 |
| Dhaulagiri I | 26794 | 13/05/1960 |
| Manaslu | 26781 | 09/05/1956 |
| Nanga Parbat | 26660 | 03/07/1953 |
| Annapurna I | 26545 | 03/06/1950 |
| Gasherbrum I | 26509 | 05/07/1958 |
| Broad Peak | 26414 | 09/06/1957 |
| Gasherbrum II | 26358 | 07/07/1956 |
| Shishapangma | 26335 | 02/05/1964 |
+---------------+------------+--------------+