Project 5
Download the file vabirds.csv.
- Create the derived type bird_data in a module bird_dat as illustrated in the example. This is still a type; we do not need a class for this exercise.
- Add the following procedures to the constructor already written.
- Write a
stats
procedure that takes only an instance of the type and returns the mean and standard deviation of the observations for that instance. - Write a minmax procedure that takes an instance of the type and the array of years and returns the maximum observed, the minimum observed, and the years for maximum and minimum. You may use the maxval, minval, maxloc, and minloc intrinsics.
- Write a main program that uses your module and also uses the sorters module that you can download ( sorters.f90). This implements bubblesort. Bubblesort is simple and slow but is more than sufficient for this exercise. Note that the subprogram is destructive, i.e. it overwrites the array to be sorted, so make a copy if you don’t want that.
- Write a
Remember to write an explicit interface for each subprogram in this “main” file. Do not use CONTAINS. Read the file name from the command line. First of all you will need to count the number of lines in the file. Write a function count_lines that does this and returns the number. It is up to you whether you pass it the number of header/footer lines.
Count_lines can check for the existence of the file and return 0 if it is not found.
Still in the read_data routine, using the number of items in the file, corrected for the header and the two footers, allocate an array of bird_data types.
Loop through this array calling your constructor for each species.
The read_data routine should return the array of years and the array of bird_data types.
Request a species name from the user. Find the species in your array of types
and print its mean, standard deviation, and results from minmax. Print some appropriate message if the species is not found. Compute an array of the means for all species.
Use the pbsort routine from sorters
to sort this array. This procedure also returns the permutation vector, which is an array of the indices of the original positions.
For example, if after the sort the permutation vector is (17,3,55,11,23, and so forth) that means that the element that was previously 17 is now the first in the new array, and so on.
Note that these sorters return in ascending order (smallest to largest). From the sorted mean array and the permutation index, print the names of the 10 most common (by mean) species over the years of observations.
Hint: you can use a trick to reverse a dimension of an array in Fortran:
R=A(ndim:1:-1)
Test the user input portion for
TurkeyVulture
TuftedTitmouse
ElegantTrogon
For this project you can require an exact match of the species name. (Note that no spaces are allowed and words are separated by capitalization; we would have to do more sophisticated string handling if we were to allow spaces and variations in capitalization.)
In addition to the sorters.f90 module mentioned above, the sample solution uses the file_utils module that collects some useful file-related subprograms, including the count_lines function.
Sample solution
module bird_dat
implicit none
type bird_data
character(len=50) :: species
integer, dimension(:), allocatable :: obs
end type bird_data
contains
subroutine constructor(bird,species,dat)
type(bird_data), intent(inout) :: bird
character(len=*), intent(in) :: species
integer, dimension(:), intent(in) :: dat
bird%species=species
allocate(bird%obs(size(dat)))
bird%obs=dat
end subroutine
subroutine stats(bird,mean,std)
type(bird_data), intent(inout) :: bird
real, intent(out) :: mean,std
real :: mu2
mean=sum(bird%obs)/float(size(bird%obs))
mu2=(sum(bird%obs)/float(size(bird%obs)))**2
std=sqrt(sum(bird%obs**2-(mu2))/float(size(bird%obs)))
end subroutine
subroutine minmax(bird,years,min_val,max_val,min_year,max_year)
type(bird_data), intent(inout) :: bird
integer, dimension(:), intent(in) :: years
real, intent(out) :: min_val,max_val
integer, intent(out) :: min_year, max_year
min_val=minval(bird%obs)
max_val=maxval(bird%obs)
min_year=years(minloc(bird%obs,1))
max_year=years(maxloc(bird%obs,1))
end subroutine
end module
program bird_obs
use bird_dat
use sorters
implicit none
!******************************************************************
!Mean observations, standard deviation, max and min of a set of
!bird observations
!Author: Katherine Holcomb
!Changelog: Initial version 2015-03-4
!******************************************************************
type(bird_data),dimension (:), allocatable :: bird_list
real, dimension(:), allocatable :: means,stds,oldmeans
character(len=50) :: filename
character(len=50) :: species,my_species,lc_species
integer :: n, nargs, nbirds
logical :: found_it
real :: std,mean,min_val,max_val
integer :: min_year, max_year
integer, dimension(:),allocatable :: years, pvec
interface
subroutine read_data(bird_list,filename,years)
use bird_dat
use file_utils
implicit none
type(bird_data), dimension(:), allocatable, intent(out) :: bird_list
character(len=*), intent(in) :: filename
integer, dimension(:),allocatable, intent(out) :: years
end subroutine
end interface
nargs=command_argument_count()
if ( nargs .ne. 2 ) then
stop "Usage: <file> species"
else
call get_command_argument(1,filename)
call get_command_argument(2,my_species)
endif
call read_data(bird_list,filename,years)
nbirds=size(bird_list)
found_it=.false.
allocate(means(nbirds),stds(nbirds))
do n=1,nbirds
call stats(bird_list(n),means(n),stds(n))
if (my_species==bird_list(n)%species) then
mean=means(n)
std=stds(n)
call minmax(bird_list(n),years,min_val,max_val,min_year,max_year)
found_it=.true.
endif
enddo
if (found_it) then
write(*,*) "Statistics for "//my_species
write(*,'(a,f8.2)',advance='no') "The minimum value of observations is:",min_val
write(*,'(a,i5)') " in year",min_year
write(*,'(a,f8.2)',advance='no') "The maximum value of observations is:",max_val
write(*,'(a,i5)') " in year",max_year
write(*,'(a,f0.2)') "The mean value of the observations is:",mean
write(*,'(a,f0.2)') "The standard deviation of the observations is:",std
else
write(*,*) "Requested species not found in the data file."
endif
allocate(pvec(nbirds))
allocate(oldmeans(size(means)))
oldmeans=means
call pshellsort(means,pvec,nbirds)
write(*,*)
write(*,*) 'The 10 most common birds are'
do n=nbirds,nbirds-9,-1
write(*,'(a,f8.2)') bird_list(pvec(n))%species(1:len_trim(species)),oldmeans(pvec(n))
enddo
End program
subroutine read_data(bird_list,filename,years)
use bird_dat
use file_utils
implicit none
type(bird_data), dimension(:), allocatable, intent(out) :: bird_list
character(len=*), intent(in) :: filename
integer, dimension(:), allocatable, intent(out) :: years
integer, dimension(:), allocatable :: obs
integer, parameter :: nobs=47
character(len=6),dimension(nobs) :: cyears
integer :: inunit
integer :: nheaders,nbirds
character(len=50) :: species
character(len=1024) :: line
character(len=:),dimension(:),allocatable :: line_vals
integer :: num_vals
integer :: n
inunit=open_file(filename,'r')
if (inunit .ne. 0) then
open(unit=inunit,file=filename)
else
stop "Unable to open specified data file."
endif
nheaders=3
nbirds=count_records(inunit,nheaders)
allocate(bird_list(nbirds))
allocate(years(nobs))
read(inunit,*) species,cyears(:)
do n=1,nobs
read(cyears(n),'(i4)') years(n)
enddo
allocate(obs(nobs))
do n=1,nbirds
read(inunit,*) species, obs
call constructor(bird_list(n),species,obs)
end do
end subroutine read_data