Python Forum
Drawing graphs with matplotlib
Thread Rating:
  • 2 Vote(s) - 3.5 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Drawing graphs with matplotlib
#1
Hi,
I am trying to write a short program to visualise population dynamics in lemming populations.
The code I have below works ok but only gives me a scatter plot (obvs as I have use pot.scatter()!!!)
What I really want is to get the scatter graph but with a line connecting all the points.
I have tried plt.plot() but cannot get more than 1 point plotted. It doesn't seem to run through the iteration.
I have tried to do def() and then use while loops but without success.
Any help on getting the iterations plotting with a line joining each point on the graph would be GREATLY appreciated!
I am a Biology teacher and would like to use this sort of model with my students.
Thanks!


import matplotlib.pyplot as plt

## Set the inital population size using user input
init_pop = float(input("What would you like to set the initial population as? (Please note the maximum population size is 1000)"))
print ("Ok, initial population set at", init_pop, " .")

## The logistic equation that this program models requires the initial population size to be a proportion of the maixmum population size
pop= init_pop/1000

##Set the number of years to model
Years = float(input("How many years do you want to run the simulation for? "))
print ("Ok, runnning the simulation for", Years, "years. " )

## Set the growth rate. Nb as this changes the population dynamics vary wildly from chaotic to stable population size
rate = float(input("What would you like to set the growth rate as?"))
print ("Ok, growth rate is set at", rate, " .")

##This is where I use the input numbers to do the maths part of the logistic equation
for x in range(int(Years)):
    Pop_new = rate*pop*(1-pop)
    pop = Pop_new
    plt.scatter(x+1, Pop_new*100)
    print (x+1, Pop_new*1000)

plt.ylabel("Population Size")
plt.xlabel("Years")
plt.show()
Reply
#2
Hey, I think I have figured out a fix. Not sure if it is the best was but I though I could get the iteration to append a list and then call the plot.plot() to print the list!
Anyway... this works for me.
Any better more python ways of doing this would be greatly appreciated.
Thanks

ps.... I stripped out all input but the changing growth rate.

import matplotlib.pyplot as plt

def New_pop(r):
    a = r*x*(1-x)
    return a


r = (float(input("What growth rate do you want? ")))
a = 200/1000
i = 1
Years =
Pop_data =

while i <=20:
    x=a
    New_pop(r)
    a = New_pop(r)
    Pop_data.append(a*1000)
    i +=1
    Years.append(i)
    print (i + 1)
    print (x*1000)

plt.plot(Years, Pop_data)
plt.ylabel("Population Size")
plt.xlabel("Years")
plt.show()
Reply
#3
My suggestion, don't limit the values and do conversion. Let the students play with non realistic values for initial population.
They'll see what happens.

A generator is a good start to calculate grow rates. Here is my example. A little bit more pythonic with error checking.
import matplotlib.pyplot as plt


def get_user_input():
    """
    A helper function to get user input with error checking.
    Returns population, years, rate
    """
    int_error_msg = 'Please enter an integer.'
    float_error_msg = 'Please enter a float.'
    while True:
        question = 'What would you like to set the initial population as?: '
        try:
            pop = int(input(question))
        except ValueError:
            print(int_error_msg)
            continue
        if pop < 0:
            print('Population must bigger than 0.')
        else:
            print('Initial population set to {}.'.format(pop))
            break
    while True:
        question = 'How many years do you want to run the simulation for?: '
        try:
            years = int(input(question))
        except ValueError:
            print(int_error_msg)
            continue
        if years < 0:
            print('The value for years must be bigger than 0.')
        else:
            print('Running the simulation for {} years.'.format(years))
            break
    while True:
        question = 'What would you like to set the growth rate as?: '
        try:
            rate = float(input(question))
        except ValueError:
            print(float_error_msg)
        print ('Growth rate is set at'.format(rate))
        break
    return pop, years, rate


def growth_rates(pop, years, rate):
    """
    Generator to calculate population series
    for the given time period in yeas with the rate.
    
    Returns (year, population)
    """
    for year in range(years + 1):
        yield year, pop
        pop += pop * rate


if __name__ == '__main__':
    population, years, rate = get_user_input()
    growth_generator = growth_rates(population, years, rate)
    data = list(growth_generator)
    # give a list [(year0, pop0), (year1, pop1), (yearn, popn), ...)
    # we need to transform the data, like transpose in excel
    years, populations = zip(*data)
    populations = [int(p) for p in populations] # convert populations back to integer

    #print(data)
    #print('After transpose')
    #print(year, population)
    plt.xlabel("Years")
    plt.ylabel("Population Size")
    plt.plot(years, populations, '-o')
    plt.show()
You can make also a GUI with tkinter or as a teacher you can use jupyter-notebooks. They are cool for teaching, presentation, documentation and your code.
Almost dead, but too lazy to die: https://sourceserver.info
All humans together. We don't need politicians!
Reply
#4
Thank you so much for this.
I totally agree that GUI with this sort of thing would be great but I am still fairly new to python and haven't got to GUIs yet!
I will look into jupiter notebooks, not heard of them.
Thank you... I am going to go through your code carefully to learn as much as I can.
Reply
#5
The interesting part is this function:

def growth_rates(pop, years, rate):
    for year in range(years + 1):
        yield year, pop
        pop += pop * rate
The yield statement is the marker, that this is not a normal function.
When the function got called, it returns instantly a generator.

g = growth_rates(1, 1, 1)
print(g)
Output:
<generator object growth_rates at 0x7fda80aba048>
A generator is lazy evaluated. When iterating over a generator, it yields
at every yield statement an object (in this case a tuple with
year and pop.

for element in g:
    print(element)
Output:
(0, 1) (1, 2)
The cool part is, that this operation makes it easier to understand the algorithm itself better.

    for year in range(years + 1):
        # begins with 0, ends with number of years
        yield year, pop
        # the first iteration yields
        # the year 0, and the initial value of population
        pop += pop * rate
        # here happens the magic. pop is multiplied with rate and the result is added
        # to pop
Beginners tend to write this as a normal function with a list and regular they don't use the in-place addition:

def growth_rates(pop, years, rate):
    return_list = []
    for year in range(years + 1):
        return_list.append((year, pop))
        pop = pop + pop * rate
    return return_list
You can see a bit more overhead in this function.
The generator has the benefit, that you outside of the generator decide which datatype is used as a container for the results.
For example the generator can called with: list, tuple, set and other functions,
which takes an iterable
(min, max, statistics.mean, statistics.median, sorted).

The trick with the built-in function zip is following, which is easier to explain it with code:
iterator = zip(list1, list2, list3, list4)
for element in iterator:
    print(element)
# list1[0], list2[0], list3[0], list4[0]
# list1[1], list2[1], list3[1], list4[1]
# list1[2], list2[2], list3[2], list4[2]
The * in front of an iterable, for example a list, unpacks the whole list.
Using this inside a zip function will do following:

data = [(0, 100), (1, 101), (2, 102)]
iterator = zip(*data)
# iterator = zip((0, 100), (1, 101), (2, 102))
# iterating over the iterator will do following
# take the first element from all lists
# then take the second element from all lists
# doing this with a normal assignment will be following result
result = list(zip(*data))
# [(0, 1, 2), (100, 101, 102)]
# remind that zip returns since python a generator object,
# without implicit or explicit iteration, you don't get values back
# with an assignment to more then one variable name on the left side,
# the statement(s) on the right side is/are evaluated
first_element, second_element = zip(*data)
I use always as explanation the Excel Transpose function: https://www.techonthenet.com/excel/formu...nspose.php
This is often easier to understand. The bult-in zip function can be used for this task.

If this is too mindlblowing, you can also do it in the classic way:
def growth_rates(pop, years, rate):
    return_list_pop = []
    return_list_year = []
    for year in range(years + 1):
        return_list_year.append(year)
        return_list_pop.append(pop)
        pop = pop + pop * rate
    return return_list_year, return_list_pop


years, pops = growth_rates(1, 1, 1)
The first function I've written for the user_input is not so important to understand it yet.
Later you will. But for data handling the generator functions, zip, concept of iteration is
very handy and produces lesser code.

You'll find with google much better tutorials about generators.

At the end, the plot function of matplotlib needs an evaluated list for y-axis as single argument or
x and y axis as two arguments. If you left out the year and just use the y values, the plot function
counts the x-axis for each element from y by itself beginning from 0. But often you want to decide
which values are used for the x-axis.

By the way, I write so much with the hope that other can find this topic.
Almost dead, but too lazy to die: https://sourceserver.info
All humans together. We don't need politicians!
Reply
#6
Thank you soooo much!!
Loads go brilliant stuff for me to go through and learn from
Thanks
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Plot several graphs on the same row Menthix 1 1,030 Mar-18-2022, 05:47 PM
Last Post: deanhystad
  grouped bar graphs tobiasfw 1 1,400 Feb-22-2022, 02:25 PM
Last Post: deanhystad
  Matplotlib: How do I convert Dates from Excel to use in Matplotlib JaneTan 1 3,216 Mar-11-2021, 10:52 AM
Last Post: buran
  Python Matplotlib: How do I save (in pdf) all the graphs I create in a loop? JaneTan 1 8,852 Feb-28-2021, 06:20 PM
Last Post: Larz60+
  How to merge strings through graphs Den 6 3,407 Jun-29-2020, 07:07 AM
Last Post: Den
  How to clear matplotlib graphs from memory mrashy 0 4,551 Apr-01-2020, 04:03 AM
Last Post: mrashy
  Show graphs in matplotlib from a sql query? ScaleMan 1 1,852 Feb-06-2020, 05:47 PM
Last Post: ScaleMan
  Tranforming into .exe with Matplotlib (graphs) ericvrocha 0 2,908 Oct-14-2019, 06:54 PM
Last Post: ericvrocha
  Little Help With Graphs ericvrocha 5 3,175 Oct-14-2019, 12:07 PM
Last Post: buran

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020