CS 152: Project 1

Title image Project 1
Fall 2019

Project 1: Creating Data?

The purpose of this project is to give you chance to break down simple problems into precise steps that solve the problem . In computer science speak, you will be developing algorithms. In this case you will write your algorithms as sequences of Python commands that calculate a value or otherwise manipulate data.

While there are lots of steps to this project, each one is very short. Make sure you understand each step and ask questions. Test out your code as you go and explore with variations to see what happens.


Tasks

  1. Set up your workspace

    If you haven't already set yourself up for working on the project, then do so now.

    1. Mount your directory on the Personal server.
    2. Open a terminal and navigate to your project1 directory on the Personal server.
    3. Open TextWrangler. If you want to look at any of the files you have already created, then open those files.

  2. Explore how Python works with numbers

    The first part of the project is to continue the example from lab of adding three numbers. You will write six different versions of the code (you have already written one) that each get increasingly more sophisticated.

    1. Version 2: Integer math

      Run your addthree.py file from lab. Notice the differences in the output of the sum and average calculations.

      You can identify that python is executing floating point math when calculating the average because decimals appeared in the answer. Since all of the inputs are integers for the sum, and the sum of integers must be an integer, that result is still an integer (no decimal points). Note that we did not include any decimals in our code, but since the result of the division was not an integer, Python automatically assumed that we wanted it to do floating point math.

      For version 2, copy your version 1 lines of code and paste them below version 1. Change the first print statement to 'version 2', and then change the division sign to a double slash: //. Now you are telling Python that the division should be intger division and the result should be an integer, not a floating point value.

      Run addthree.py again and see if it gives you a different answer than version 1.

      Testing your code: Sometimes your code doesn't work. The first thing to do is to carefully read any error message. It should direct you to the line of the file that failed to work. Some common errors include the following.

      • Syntax error: Forgetting the colon at the end of a function definition line.
      • Syntax error: Forgetting to pair all parentheses (Python really hates it when you have more left/open parentheses than right/close parentheses).
      • Typo: Misspelling a python command, variable, or function name
      • Tabbing/White Space error: Having inconsistent tabbing. All code should be lined up carefully. The main code should have no spaces or tabs at the beginning of the line. The code "inside" a function definition should be tabbed in once, with all lines tabbed in the same amount.

        Note that Python considers tabs and white space to be different things, which is sometimes hard to debug because all of your code looks correct. If you think your code is correct, then select all of your code (cmd-A) and then choose Text::Entab in TextWrangler to convert all of your white space to tabs or Text::Detab to convert all of your white space to spaces. That will generally correct the problem if the error is mis-matched white space.

    2. Version 3: Mixed number types

      Copy your version 2 code and paste it at the bottom of the file. Change the comment and first print statement to 'version 3'. Add a .0 to the 5 in the sum line and to the 3 in the average line.

      Run addthree.py again and see what answer it gives you.

      The lesson from version 3 is that when Python does a computation, it uses the most flexible representation present in the mathematical expression to hold the result (unless you tell it not to). In the case of the integer division operator, Python does the integer division specified, but it then returns a result that is a floating point value.

    3. Version 4: Using variables to hold values

      If we wanted to use the program to add a different three numbers, how many places to we have to change the code? Because we have to change all instances of a number everywhere in the code, we have to change each number in two different places. From a coding point of view, that is a bad idea. It is inefficient (takes too much time and effort) and prone to create errors in the code (lots of opportunities to type the wrong thing).

      To make our code more efficient, start a version 4 at the bottom of the file. Make three assignments, assigning to the variable a the number 42, assigning to variable b the number 21, and assigning to variable c the number 5. The equals sign is the assignment operator and it copies the information on the right side of the assignment to the variable on the left side of the assignment. So to assign to the variable a the number 42 you would write the following code:

      a = 42

      Another way of describing that statement is to say the a gets the value 42.

      After you have made the three assignments, you can use the variables a, b, and c in mathematic expressions, just like we used 42, 21, and 5 in the prior versions.

      Have python print out the sum of the three variables. Just change the expression 42 + 21 + 5 in version 1 to a + b + c for version 4. Do the same for the average, using the expression (a + b +c) and then dividing that expression by 3.0

      Make sure your code prints out the same set of values as version 1.

    4. Version 5: Getting input from the user

      In version 4, if we want to add a different set of three numbers, we still have to edit the code. Version 5 will let us enter the numbers on the terminal when we run the program, letting us add any three numbers without changing the code.

      Copy version 4 to the bottom of the file. But instead of assigning numbers to the variables a, b, and c, assign the result of calling the input function. Your first assignment might look like the following. Modify the other two to assign b and c.

      a = input("Enter first number :")

      The input function prints out the prompt and then waits for the user to type something and hit the return key. In this example, whatever the user types after the prompt is stored in the variable a.

      What happens when you run this program? Is Python happy with the last line of code?

      The problem with this program is that input returns a string of characters, not a number. Therefore, we have to convert the string into a number before we can add and divide the numbers properly. This is a process called casting. The following assignment converts the string in variable a into an integer and then assigns the integer back to the variable a, overwriting its old contents.

      a = int(a)

      Modify version five so that it converts the three variables to integer values before executing the sum or average expressions.

      Note that you can find out more about a function like input by using the Python help function. If you start python in a terminal (type python and hit return), then you can ask Python for help about functions and types. If you type

      help(input)

      then Python will give you more information about the function, including the fact that it returns a string. You can type 'q' to get out of the help function. Likewise, if you type:

      help(int)

      then Python gives you a lot of information about the int type, including that it will convert a number or string to an integer. Again, type 'q' to exit the help viewer. To exit the Python interpreter, you can use control-d, or you can type exit().

    5. Version 6: Using a function with parameters

      A function is a block of code to which we attach a name. By using the name, we can tell Python to execute the code. This makes it easier to do tasks that we want to repeat.

      A function starts with the keyword def, followed by the name of the function. You can name a function anything you like, but it is helpful to pick a meaningful name. After the function name comes a list of function parameters in parentheses. The function definition line ends with a colon.

      Start this function using the following line.

      def stats( a, b, c ):

      All of the code inside a function must be tabbed in relative to the function definition line. Copy the last three lines of your version 4 code to just after the function definition. Then tab in all three lines. You can tab the lines one by one or you can select all three lines and then use the Text::Shift Right menu command to shift the whole block right.

      Defining a function does not execute the code inside the function. In order to execute the code in a function we have to call the function. To call a function, use the name of the function, followed by parentheses with the function arguments. In this case, there are three function arguments, so you need to put three numbers, separated by commas, inside the parentheses.

      After the function, and not tabbed in, put the following code

      stats( 42, 21, 5 )

      The code above calls the function stats with the arguments 42, 21, and 5. The 41 is assigned to a, the 21 is assigned to b, and the 5 is assigned to c.

      Run your code. Version 6 should give you the same result as version 4.

      Add another call to the stats function to your code, but give it three different numbers. Can you see how functions might be useful?

  3. Generating Data with Functions

    The second part of this project is to generate some data and then plot it using your favorite spreadsheet (Excel, Google Sheets, Numbers). In particular, you're going to implement the equations for a ballistic trajectory given an initial velocity and position and write out (x, y) locations for an object over time.

    The following physics equation computes the position p_f of an object given its initial position p_i, initial velocity v_i, the acceleration acting on the object a, and the time duration t. The acceleration is assumed to be constant. It is a reasonable model for calculating the position of a thrown object being acted on by gravity.

    In the following steps we'll write a function using code that implements this equation.

    1. Create a new python file

      Make a new file in your text editor and save it as gendata.py. Put your name and the course name at the top of the file.

    2. Identify the variables

      The first step is to identify all of the elements of the equation. There are five variables in this equation. The variable pf on the left side of the assignment is what we want to compute. The variables pi, vi, t, and a are the values we need to do the computation.

    3. Write the statement in code

      In the statement there are one assignment, two additions, three multiplications, and one square operation.

      1. Start with the left side of the assignment and assign to pf the value pi.
      2. Continue the expression on the right side by adding (plus sign) vi times t. In most programming languages, multiplication is the star symbol * (shift-8).
      3. Finally, add (plus sign) the final term which can be expressed as 0.5 times a times t squared. To square the t value you have two options: (1) use the expression t*t or use the exponentiation operator ** and use the expression t**2.

    4. Test your code

      Before your assignment statement, assign to pi the value 1, assign to vi the value 11, assign to a the value -10, and assign to t the value 0.5. After the assignment statement, print the value of pf.

      Run your program. It should print out the value 5.25.

    5. Encapsulate the expression in a function

      Define a function named ballistic1 with the parameter t. Use the keyword def before the function name, and put the parameter in paretheses after the name. Don't forget to end the line with a colon.

      Move the code you wrote (the assignments, the equation statement, and the print statement) so it is inside the function. It should come after the function definition and be tabbed in relative to it.

      Remove the assignment of the variable t since it is now a parameter of the function.

    6. Test your code

      After the function, call the function twice. The first time, use 0.5 as the argument (as in the code below). The second time, use 1.0 as the argument. When you run your code, it should print out 5.25 and then 7.0.

      ballistic1(0.5)

    7. Have your function return the result instead of printing it

      A return statement sends one or more values back to the calling program. A return statement does not print anything to the terminal; it moves data from a function back to its parent context. A function terminates when it reaches a return statement. Any code after a return statement will not be executed.

      To return a value from a function, use the keyword return followed by the value you want to return. In this case, you want to return the value in the variable pf. Remove the print statement and return pf at the end of your function.

      If you run your program now, it will not print anything. In order to make use of the return value, you have to assign the result of the function to a variable in the parent context, as in the example below.

      y = ballistic1(0.5)

      Once the return value from ballistic1 is assigned to the variable y, you can print its value as follows.

      print("f(0.5) is", y)

      Do the same thing for the second call to ballistic1. You can re-use the variable y. Test your code. What do you expect it to print?

    8. Write a more general ballistic function

      The function ballistic1 can compute only one ballistic trajectory. Define a function ballistic2 with four iputs: pi, v1, a, and t. It should look like ballistic1, but without the variable assignments. Only the main assignment using the equation should remain. The function should still return pf.

      Test the ballistic2 function by calling it with the parameters 1, 11, -10, and 0.5 and assigning the result to a variable (remember, assignments copy data from right to left). Print the value returned by the function. It should be 5.25.

      Test the ballistic2 function by changing the initial position pi from 1 to 2. The result should be 6.25. How many ballistic trajectories could you compute with this function?

    9. Write a function that computes a point and prints it out

      Define a new function computeAndOutput. The function should have four parameters that are the same as the ballistic2 function. This function should call the ballistic2 function with the four parameters as arguments, assign the return value to a variable (e.g. y, then print out the time t, a comma, and the computed y value.

      print( t, ",", y )

    10. Write a function that computes ten points on a trajectory

      Define a new function, trajectory10. This function have four parameters that represent initial position pi, velocity v, acceleration a, and the start time ti.

      The function should make ten calls to computeAndOutput, specifying pi, v, and a as the first three arguments. The fourth argument should be ti + N*0.1, where N is the consecutive numbers from 0 to 9. If you work it out, this means the time parameter will have the value ti, ti+0.1, ti+0.2, ti+0.3, and so on in consecutive calls to the function.

      Once your function is complete, put a call to the trajectory function at the end of your code. Pass in the values 1, 11, -10, 0. Put a second call to the trajectory function and pass in the values 1, 11, -10, 1. It should print out two columns of numbers that start with 0, 1.0 and end with 1.9, 3.85 (roughly). You can comment out your other print statements.

    11. Write a function that computes 100 points on a trajectory

      Define a function, trajectory100. This function should have four parameters that have the same meaning as those for trajectory10. This function should make ten calls to trajectory 10, increasing the time by 1 with each call.

      Test your code using the parameters 1, 50, -10, 0. The final line should be 9.9, 5.95 (roughly).

    12. Plot your data

      You can copy and paste from the terminal into a spreadsheet, or you can save the terminal printout to a file. You can redirect the output of a program into a file by using the terminal > operator.

      python3 genData.py > mydata.csv

      You can then open the CSV file in a spreadsheet program.

    13. Update your computeAndOutput function so that it writes the data to a file

      While using the terminal's redirect capability is nice, it's also nice to be able to write directly to a file. That way you can still have your program print useful messages without having it clutter your data.

      In your computeAndOutput function, before the return statement, write the code to open a file and assign the file reference to a variable. For example:

      fp = open( 'data.csv', 'a' )

      The open function tries to open a file with the given name. The second argument specifies how to open the file. If you use 'r', then it opens the file to read, and the file must already exist. If you use 'w', it opens the file to write, creating the file from scratch and deleting any content if the file already existed. If you use 'a', then it opens file or creates a new file and appends new material to the end of the file. We're using the 'a' option because we want to add lines of data to the file.

      Next, use the write method of the file reference to add a line of data with the computed final position (e.g. y) and t to the file. Make sure you write the return value of the function, whatever name you gave it.

      fp.write( str(t) + "," + str(y) + "\n" )

      Just like when we had to convert from strings to numbers, now we have to convert a number to a string. Once we do that, we can construct a string with two numbers separated by commas followed by a newline "\n". The plus operator will concatenate strings together. The fp.write() function writes the given string to the end of the file.

      Finally, close the file.

      fp.close()

      Test your code by running the file. It should create a new file called data.csv with 100 data points. Note that if you run it a second time, it will just add more data to the file. Therefore, you may want to clear the existing file before calling trajectory100(). You can do this by using the following two lines of code.

      fp = open( 'data.csv', 'w' )
      fp.close()

      If you want, put those two lines of code in a function called clearfile. You can even make the function general by using a parameter for the filename (small extension).

  4. Explore your data generator

    Generate and plot two other trajectories with different initial conditions or acceleration. Include the three plots in your report. For example, compare trajectories for the same initial conditions on different planets: Planetary Fact Sheet


Follow-up Questions

  1. What is a variable?
  2. What is a function and how are they useful?
  3. What is a parameter and how do they affect the generality of a function?
  4. What is the difference between a print statement and a return statement?

Extensions

Projects are your opportunity to learn by doing. They are also an opportunity to explore and try out new things. Rather than list out every activity for a project, the defined part of each project will be about 85% of the assignment. You are free to stop there and hand in your work. If you do all of the required tasks and do them well, you will earn a B/B+.

To earn a higher grade, you can undertake one or more extensions. The difficulty and quality of the extension or extensions will determine your final grade for the assignment. Extensions are your opportunity to customize the assignment by doing something of interest to you. Each week we will suggest some things to try, but you are free to choose your own.

A common question is "how much is an extension worth?" The answer to that question depends on the extension, how well it is done, and how far you pushed it. As teachers, we prefer to see one significant extension, done well, where you explore a topic in some depth. But doing 2-3 simpler extensions is also fine, if that's what you want to do. Choose extensions that you find interesting and challenging.

The following are a few suggestions on things you can do as extensions to this assignment. You are free to choose other extensions, and we are happy to discuss ideas with you.


Submit your code

Turn in your code (all files ending with .py) by putting it in a directory in the Courses server. On the Courses server, you should have access to a directory called CS152, and within that, a directory with your user name. Within this directory is a directory named private. Files that you put into that private directory you can edit, read, and write, and the professor can edit, read, and write, but no one else. To hand in your code and other materials, create a new directory, such as project1, and then copy your code into the project directory for that week. Please submit only code that you want to be graded.

When submitting your code, double check the following.

  1. Is your name at the top of each code file?
  2. Does every function have a comment or docstring specifying what it does?
  3. Is your handin project directory inside your Private folder on Courses?


Write your project report

For CS 152 please use Google Docs to write your report. Create a new doc for each project. Start the doc with a title and your name. Attach the doc to your project on Google classroom. Make sure you click submit when you are done. The graders cannot provide feedback unless you click submit.

Your intended audience for your report is your peers not in the class. From week to week you can assume your audience has read your prior reports. Your goal should be to be able to use it to explain to friends what you accomplished in this project and to give them a sense of how you did it.

Your project report should contain the following elements.