Assignment 3: Sunrise, Sunset

Assigned: Thursday, 15 September
Due: Wednesday, 21 September, 11:59 p.m.

We love sunny summer days, but as we move into fall and then winter, we know the hours of daylight begin to dwindle. In this assignment, we’ll use our experience working with tables (in class and in lab) to process a table of solar and lunar temporal information and visualize how the hours of sunlight and moonlight vary over time.

Some kindly astronomers have shared a data set with us, which they’ve made available as a Google sheet.

Task: Copy the unique ID – the long string of letters, numbers, and underscores – in the spreadsheet URL and use it to load it into a Pyret table named celestial-data. Refer to the code for loading a table from a spreadsheet, seen in class and in lab.

To ensure the autograder works for your program, use the column names

year, month, day,
age-of-moon,
sun-rise, sun-culm, sun-set,
moon-rise, moon-culm, moon-set

We won’t use all of the columns in this spreadsheet. This is perfectly normal when working with real data sets!

Task: To test the functions we’ll write, we also need a small table of test data. Use the following table or make up your own examples:

test-data =
  table:
    year, month, day,
    sun-rise, sun-set,
    moon-rise, moon-set
    row: 2021, 9, 30, "06:30", "18:30", "00:00", "16:00"
    row: 2022, 3, 22, "07:00", "18:00", "23:00", "08:30"
  end

Warning: Be sure to write examples in a where block for all of the functions you define. (The autograder won’t deduct points for missing tests, but your instructor will!)

To compute the hours of sunlight, we’ll need to know the elapsed time between sunrise and sunset. It’s complex to do this with times expressed as hours and minutes, e.g., "06:30", so we’ll first write a function to convert a time to minutes.

Task: Write a function

fun mins-since-midnight(time :: String) -> Number:
  ...
end

Given a time as a string (like those in the table), it should convert it to the number of minutes since midnight.

Hints:

  • Since the times are strings, you may want to review the documentation listing the functions Pyret provides for working with strings. How could you find just the hours in the string? Just the minutes?

    Some of the string functions you’ll see in the documentation produce a List, which is a data type we haven’t worked with yet. You don’t need to use Lists to solve this problem!

  • After you’ve identified the hours and minutes, you’ll need to use the function string-to-number. Despite its name, the output is not a number! It’s some number if the string can be converted and none if it can’t. For this assignment, the strings can always be converted to numbers, so you only need to worry about the some case. To get the number out, use .value:

››› string-to-number("06")
some(6)
››› string-to-number("06").value
6

Now that you can convert the sunrise and sunset times to minutes since midnight, it’s not too hard to compute how many minutes are in between.

Task: Write a function

fun elapsed-mins(start-time :: String, end-time :: String)
  -> Number:
  ...
end

It should compute the minutes the elapsed between the given start time and end time, provided as strings like those found in the table.

And now we’re ready to add a new column to the table containing the hours of sunlight.

Task: Make a table named celestial-data-sun that has a new column called "sunlight" that contains the number of hours of sunlight for each day.

Remember: To do this, you’ll need to define a function that you’ll pass as an input to build-column, and it will need to convert the elapsed minutes to hours.

Now that we’ve computed the hours of sunlight, it seems like it should be simple to compute the hours of moonlight – just swap the column names "sun-rise" and "sun-set" for "moon-rise" and "moon-set".

This works for some of the rows in our table:

››› good-night = test-data.row-n(0)
››› elapsed-mins(good-night["moon-rise"], good-night["moon-set"])
960

But not for others:

››› bad-night = test-data.row-n(1)
››› elapsed-mins(bad-night["moon-rise"], bad-night["moon-set"])
-870

Why is this? The moon rose at 23:00 (i.e., 11 p.m.) on one day and set at 8:30 a.m. the next.

Let’s extend our code to handle this!

Task: Write a function

fun mins-before-midnight(time :: String) -> Number:
  ...
end

Don’t repeat the code from mins-since-midnight! Instead, think how you can use a call to that function in your definition. The body of this function only needs to be one line!

Now we can improve elapsed-mins. Let’s leave our original version (which works for sunlight) and define a new one:

Task: Write a function

fun elapsed-mins-improved(start-time :: String, end-time :: String)
  -> Number:
  ...
end

When the start time is before the end time, it should work just like elapsed-mins.

When the start time is after the end time (that is, it’s the next day), the function should instead take the minutes from the start time until midnight (which you just wrote a function to do) and add them to the minutes from midnight until the end time.

With elapsed-mins-improved, we’re ready to compute the hours of moonlight for each day in the table.

Task: Make a table named celestial-data-sun-moon that has a new column called "moonlight" that contains the number of hours of moonlight for each day.

Be sure you are starting with celestial-data-sun from Part 1! We want to end up with a single table that has columns for both hours of sunlight and hours of moonlight.

While we can look at the numbers we added to the table, it’s much nicer to get a graphic overview.

We’ll draw bar charts, but, unfortunately, none of the columns in the table give a good label for the bars. Since each bar is for one day, they should be labeled with the date.

Task: Make a new table, celestial-data-to-plot, that has a column "date" giving the abbreviated date for each row, e.g., "9/30" for September 30.

Task: Display bar charts for the hours of sunlight and moonlight. To do this, call

bar-chart(celestial-data-to-plot, "date", "sunlight")

and

bar-chart(celestial-data-to-plot, "date", "moonlight")

If everything’s worked right, you should see a very clear trend for the hours of sunlight and a very clear repeating pattern for hours of moonlight!

As with Assignment 2, you are expected to follow good Pyret style, including writing docstrings and examples for each function. Review the Testing and Style Guidelines and ask questions if anything’s unclear!

  1. Download your file (FileDownload) and ensure it’s named asmt03.arr.

  2. Upload your assignment on Gradescope.

Note: You can submit as many times as you want before the deadline. Only your latest submission will be graded.