Sunday, October 31, 2021

ItsMyCode: Python pip: command not found Solution

ItsMyCode |

Pip is a recursive acronym for either “Pip Installs Packages” or “Pip Installs Python.” Alternatively, pip stands for “preferred installer program.” Basically, it is a package manager that allows you to download and install packages. If you try to install packages without pip installed, you will get an error pip: command not found.

In this article, we will look at the cause of this error and possible solutions to fix this error which you are encountering.

pip: command not found

The issue differs based on the environment and the os which you are using. Let’s understand how the pip is packaged into different environments.

Error Message from Bash:

bash: pip: command not found

Error Message from DOS command line:

'pip' is not recognized as an internal or external command,
operable program or batch file.
  1. Linux – If you have installed Python on a Linux environment, the pip does not come with Python, and you need to install pip package manager as a separate package. Hence in Linux, if you try to install a package, you will get a pip: command not found error as there is no pip package installed.
  2. Mac – On a mac, if you install the latest version of Python 3.x, you don’t have to worry about installing it separately. It comes shipped with Python distributable.
  3. Windows – On windows again, you don’t have to install the pip separately. It comes with Python distributable.

As you already know, Python 2 has reached the the end of life, which means it is no longer actively maintained and supported. If you are still using Python 2, then you should consider moving it to Python 3 and it comes with pip3.

How to check if pip is installed correctly?

The first and foremost thing to do is to check if you have already installed pip in your machine. In windows, you can check if the pip is located in the below directory. So just navigate to the directory and do check for pip.exe or pip3.exe files. If it’s not present, then you need to install it.

Check here for pip3.exe:

C:\Users\YOUR_USERNAME\AppData\Local\Programs\Python\Python36\Scripts

Note: Replace the YOUR_USERNAME with your actual username.

Installing pip the right way

1) On windows, run your Python installer once again and ensure you check the install pip checkbox in the wizard as shown in the below image.

Install pip on windowsInstall pip on windows

2) On Linux, you can install pip3 by running an apt-get command in your terminal.

sudo apt-get -y install python3-pip

Once you have run this command, you should use the pip3 package manager commands to download the packages. 

3) On Mac, pip is bundled with the Python distributable, so you need to re-install Python once again by executing the below command. Once you have re-installed Python 3, you should be able to execute pip commands.

brew install python3

Installing pip for Python 2

If you are still working on Python 2 and want to install an older version of a pip, you can install it by running the below command on your Linux machine.

sudo easy_install pip

This command installs the pip command onto your system. If you do not already have easy_install installed, install it using the following Linux command:

sudo apt-get install python-setuptools

The post Python pip: command not found Solution appeared first on ItsMyCode.



from Planet Python
via read more

ItsMyCode: Python FileNotFoundError: [Errno 2] No such file or directory Solution

ItsMyCode |

In Python, when you reference a file, it needs to exist. Otherwise, Python will return a FileNotFoundError: [Errno 2] No such file or directory.

In this tutorial, let’s look at what is FileNotFoundError: [Errno 2] No such file or directory error means and how to solve this in your code.

Python FileNotFoundError: [Errno 2] No such file or directory

Python will raise FileNotFoundError when you use the OS library and try to read a file or write a file that does not exist using an open() statement.

It is, of course, excluding you are creating a new file and writing content to the file. Any error message which states FileNotFoundError means that Python cannot find the path of the file you are referencing.

Example FileNotFoundError

The below code will list all the files in a specified folder. We will be using the OS module and os.listdir() method to get a list of files in the specified folder.

import os
for f in os.listdir("/etc"):
        print(f)

Output

Traceback (most recent call last):
  File "Main.py", line 2, in <module>
    for f in os.listdir("/etc/test"):
FileNotFoundError: [Errno 2] No such file or directory: '/etc/test'

Now you can see that Python is throwing FileNotFoundError: [Errno 2] No such file or directory since the folder reference is wrong here.

The possible reasons for this error could be as follows.

Misspelled file name

The error will often occur due to misspelled filenames, so providing the correct file name would solve the issue.

Invalid file path or directory path

Sometimes you might give a wrong file path or directory path which does not exist. It usually happens even with the network path when it’s unreachable. So ensure that the file path is correct and if you are placing the file in the network path, make sure it’s reachable and accessible.

Using a relative path

If you use a relative path, the file would be searched in the current working directory and not in the original path. So ensure you give an absolute path of the file to resolve the error.

Solution to FileNotFoundError: [Errno 2] No such file or directory

We will correct our above code by referencing the proper directory where the file exists. This time, we will also use an absolute path instead of a relative path to ensure it’s referencing the correct directory.

import os
for f in os.listdir("C:/Projects/Tryouts/etc"):
        print(f)

Output

python.txt
index.md
Python Data Science Ebook.pdf

The post Python FileNotFoundError: [Errno 2] No such file or directory Solution appeared first on ItsMyCode.



from Planet Python
via read more

The Python Coding Blog: Practise Using Lists, Tuples, Dictionaries, and Sets in Python With the Chaotic Balls Animation

One of the early topics covered when learning to code deals with the built-in data structures in Python. Lists are usually learned early on, followed by dictionaries and tuples. Sets are not normally one of the earliest topics covered. However, that’s not because they’re complex but because they’re used less often in Python. Understanding the similarities and differences between these data structures is important. But there’s more than just the ‘rules’ when using lists, tuples, dictionaries, and sets in Python.

In this article, you’ll write a simulation using lists, tuples, dictionaries, and sets in Python. The code will produce this animation:

The article’s principal aim is to practise using lists, tuples, dictionaries, and sets in Python and understand how each one is suited for a different purpose. The main purpose of the post is not to give a detailed explanation of the data structures. However, I’ll briefly review the basics of these built-in data structures in Python throughout the article.

You can find a lot more detail about lists in the Chapter about loops and lists in The Python Coding Book, and dictionaries and tuples are dealt with in the Chapter about data types.

Introducing the Chaotic Balls Animation

Have a look again at the video above showing the simulation in action. Can you guess the rules the simulation is following?

Here they are:

  • The screen includes several tiles. There are three types of tiles that are identified by the colour of the outline: green, orange, or red.
  • A ball appears randomly on the screen once every two seconds. Its direction of travel is also random.
  • When a ball hits a green tile, it speeds up.
  • When a ball hits a red tile, it slows down. When a ball slows down to zero, it disappears from the screen.
  • When a ball hits an orange tile, it changes its direction of travel randomly.
  • The colour of each ball indicates the ball’s speed.

You’ll use the turtle module for this animation. This module is part of the standard library, so you don’t need to install it separately. You don’t need to have any previous experience with the turtle module to get the most out of this article. It’s quite simple to use, and I’ll explain how it works throughout the article.

You’ll also need two other modules from the standard library: random and time.

A Quick Review of Lists and Tuples

Lists and tuples have many similarities. They’re both sequences in which the items are stored in order and can be referenced using an index showing the item’s position in the sequence:

>>> some_list = [4, 6, 7, 3, 2, 10, 4]
>>> some_tuple = (4, 6, 7, 3, 2, 10, 4)

>>> some_list[3]
3
>>> some_tuple[3]
3

>>> some_list[2:5]
[7, 3, 2]
>>> some_tuple[2:5]
(7, 3, 2)

>>> for number in some_list:
...    print(number)
...    
4
6
7
3
2
10
4

>>> for number in some_tuple:
...    print(number)
...    
4
6
7
3
2
10
4

Note that when creating a tuple, the parentheses () are optional. The following line creates the same tuple as the one in the example above:

>>> some_tuple = 4, 6, 7, 3, 2, 10, 4
>>> some_tuple
(4, 6, 7, 3, 2, 10, 4)

The key difference between lists and tuples is that lists are mutable while tuples are immutable:

>>> some_list[2] = 100
>>> some_list
[4, 6, 100, 3, 2, 10, 4]

>>> some_tuple[2] = 100
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

You can change, add, and remove items in a list, but you cannot do the same with tuples. Tuples are useful when you want to create a group of items that will not change in your code. You use a list for a container that’s meant to be flexible.

Getting Started: Setting Up the Animation

Let’s start setting up the animation and see where you need to use lists and where tuples would be more suitable. You can start by creating a window using the turtle module and selecting its size and colour:

import turtle

# Parameters to set up animation
background_colour = 50, 50, 50
screen_size = 800, 800

# Create window
window = turtle.Screen()
window.tracer(0)
window.colormode(255)
window.setup(*screen_size)
window.bgcolor(background_colour)

# Temporary line to keep window open. We'll remove later
turtle.done()

The name background_colour stores the red, green, and blue (RGB) values representing the background colour. You can use a tuple to store RGB colour triplets. You also use a tuple for the width and height of the window, which you store in screen_size.

The Create window section uses Screen() from the turtle module to create the window. The tracer() method is used to control when things are drawn on the screen. Setting this to 0 means that you’ll be able to control when to refresh the screen by using the update() method later on. The colormode() method allows you to choose to represent colours as triplets of numbers between 0 and 255 to represent the RGB values.

setup() is the method you can use to set the size of the window. This method needs two arguments to represent the width and height of the window in pixels. Therefore, you use the unpacking operator * to unpack the tuple screen_size into the two numbers that it contains. window.setup(*screen_size) is the same as window.setup(screen_size[0], screen_size[1]) in this case since there are two items in screen_size.

Finally, you change the window’s background colour using bgcolor() which accepts a tuple with RGB values as an argument. When you run this code, you should see a square window with a grey background.

Creating the Balls

You can now set things up to create the balls that appear randomly on the screen at regular intervals. You’ll use a Turtle object from the turtle module for each ball. However, you want to store all the ball objects in the same place in your program. The data structure should be iterable so that you can go through it using a loop to deal with all the balls.

You also need the container to be flexible since you’ll add a new ball every two seconds, and you need to remove balls that have come to a standstill. This is an ideal scenario for creating a list. You can initialise an empty list, ready to store the balls as they are created. You can then define a function create_new_ball() to create a ball at a random position and orientation:

import random
import turtle

# Parameters to set up animation
background_colour = 50, 50, 50
screen_size = 800, 800

# Create window
window = turtle.Screen()
window.tracer(0)
window.colormode(255)
window.setup(*screen_size)
window.bgcolor(background_colour)

# Create balls
balls = []

def create_new_ball():
    ball = turtle.Turtle()
    ball.penup()
    ball.shape("circle")
    ball.pencolor("white")
    ball.setposition(
        random.randint(-screen_size[0] // 2, screen_size[0] // 2),
        random.randint(-screen_size[1] // 2, screen_size[1] // 2)
    )
    ball.setheading(random.randint(0, 359))
    ball.ball_speed = 0.5

    balls.append(ball)

create_new_ball()  # Start animation with one ball

# Temporary lines. We'll remove later
window.update()
turtle.done()

Once you create an instance of the turtle.Turtle class, you call several of its methods:

  • penup() ensures that no lines are drawn when a Turtle object moves
  • shape() changes the shape of the object displayed. Here, you’re changing the shape to a circle.
  • pencolor() selects the colour of any lines drawn by the Turtle. As you called penup() earlier, no lines will be drawn. However, the outline of the circle displayed will now be white.
  • setposition() moves the Turtle object to the x– and y-coordinates given as arguments. The centre of the screen is represented by the coordinates (0, 0). Therefore, the first argument is a random number between -400 and 400 since the width is 800. The second argument follows the same principle but uses the height of the window, which is the second item in screen_size. You use floor division // to ensure the result is an integer as random.randint() needs integer arguments.
  • setheading() changes the orientation of the Turtle object. You will use the forward() method later, which will move the Turtle object in the direction the object is facing.

ball_speed is not an attribute of the Turtle class. You’re creating an instance variable with the line ball.ball_speed = 0.5. If you want to brush up on this topic, you can read more about Python instance variables. Each ball will have its own speed as balls will speed up or slow down at different rates.

Each time you call create_new_ball(), the program will create a new Turtle representing a ball and add it to the list balls. You call the function once right away so that there’s one ball at the start of the animation. You’ll call the function again later to create more balls. For the time being, when you run this code, you see a single, stationary ball placed in a random position on the screen. The ball has a white outline since you set this to white when you called pencolor(). The rest of the ball is black, which is the default colour. You’ll change this colour later:

It’s now time to add movement to the animation.

Creating the Main Loop to Move the Balls

All animations will need a main loop to run through each frame of the animation. You can use a while True loop in this case. Although you only have one ball in the animation, you know that all the balls will be represented by Turtle objects stored in the list balls. Therefore, you can iterate through this list in the while loop to move the balls. You can also take care of what happens when the ball leaves the window from either of the four edges: left, right, top, or bottom. Here are the additions you’ll need to your code:

import random
import time
import turtle

# Parameters to set up animation
background_colour = 50, 50, 50
screen_size = 800, 800

# Create window
window = turtle.Screen()
window.tracer(0)
window.colormode(255)
window.setup(*screen_size)
window.bgcolor(background_colour)

# Create balls
balls = []

def create_new_ball():
    ball = turtle.Turtle()
    ball.penup()
    ball.shape("circle")
    ball.pencolor("white")
    ball.setposition(
        random.randint(-screen_size[0] // 2, screen_size[0] // 2),
        random.randint(-screen_size[1] // 2, screen_size[1] // 2)
    )
    ball.setheading(random.randint(0, 359))
    ball.ball_speed = 0.5

    balls.append(ball)

create_new_ball()  # Start animation with one ball

# Main animation loop
while True:
    for ball in balls:
        # Move ball
        ball.forward(ball.ball_speed)
        # If ball goes out of bounds, move to other side
        if abs(ball.xcor()) > screen_size[0] / 2:
            ball.setx(-ball.xcor())
        if abs(ball.ycor()) > screen_size[1] / 2:
            ball.sety(-ball.ycor())

    window.update()
    time.sleep(0.001)

You’ve now added a while loop. Each iteration of this loop represents one frame of the animation. The while loop so far consists of the following:

  • A for loop that iterates through the list containing all the Turtle objects representing the balls
  • The call to the forward() method of the Turtle class. This method moves the Turtle forward by the number of pixels given as an argument. The argument is ball.ball_speed. The instance variable ball_speed is one you have created in create_new_ball() and each Turtle will have its own value. The Turtle will move in the direction it is facing, which you’ve set to a random value in create_new_ball().
  • Two if statements. These statements are needed to check whether the ball has left the screen through any of the four sides.
    • The methods setx() and sety() are similar to setposition() which you used earlier. However, they only change one of the Turtle object’s coordinates at a time instead of both.
    • The methods xcor() and ycor() return the Turtle object’s x– and y-coordinates.
    • The abs() built-in function returns the absolute value of its argument. In this case, as the value will be a float, the function will always return the positive value of the difference between the ball’s coordinate and the screen’s half-width or half-height. This allows you to test for the left and right edges in the same statement and for the top and bottom edges in another.
  • The call to update() refreshes the display on the screen. This method is used with tracer(0) to control when things are drawn in the animation. By placing window.update() in the while loop, you refresh the image once per frame.
  • time.sleep() introduces a small delay in the loop. You’ll need to import the time built-in module, too. In this animation, you’re not controlling the speed of each frame strictly. Instead, your while loop will run at whatever speed your computer allows it to! This means that the speed of the animation will vary from computer to computer. Putting in a small delay in the while loop allows you to control the overall speed of the animation. Change the value used as an argument for time.sleep() to suit your computer speed. If your animation is too fast, use a larger number.

You’ve also removed the temporary lines you had at the bottom of your code earlier. You no longer need these lines now that the while loop is in place.

This code gives output similar to the following video:

The ball will appear in a random position and move in a random direction. It should reappear at the opposite end of the screen whenever it leaves the window through any of the four edges.

Creating a Grid

You can now turn your attention to creating the tiles. You can create a virtual grid and work out how the grid maps to the whole screen. In the code below, you’ll create a 16x16 grid. Since the screen is 800x800 pixels, each cell of the grid will be 50x50 pixels, since 800÷16=50.

However, you don’t want every one of the 16x16 cells of the grid to include a tile. In the animation, there are gaps where there are no tiles. You can now define some parameters at the top of your code to set up the grid:

import random
import time
import turtle

# Parameters to set up animation
background_colour = 50, 50, 50
screen_size = 800, 800

grid_size = 16, 16
grid_scale = (
    screen_size[0] / grid_size[0],
    screen_size[1] / grid_size[1]
)
fraction_of_grid_points_used = 0.35
n_tiles = int(
    fraction_of_grid_points_used * grid_size[0] * grid_size[1]
)

# ...

grid_size and grid_scale are both tuples containing two values representing the x– and y-values. grid_scale contains the size in pixels of each cell in the grid. In this example, this is 50x50.

You’ve then set the value for fraction_of_grid_points to 0.35. This means that 35% of all the 16x16 grid cells will be filled with tiles. The result of this calculation is stored in n_tiles.

A Quick Review of Dictionaries and Sets

In this tutorial, you’re practising using lists, tuples, dictionaries, and sets in Python. You’ve already used lists and tuples. Now, it’s time for a quick review of the other two data structures you’re using in this example.

A dictionary is a mapping linking a key to a value. Each item in a dictionary consists of a key-value pair:

>>> some_dictionary = {"James": 10, "Mary": 20, "Kate": 15}
>>> some_dictionary["James"]
10

The values of a dictionary can be of any data type, including other data structures. The values can also be function names:

>>> another_dict = {"first": print, "second": str.upper}
>>> another_dict["first"]
<built-in function print>

>>> another_dict["first"]("hello")
hello

>>> another_dict["second"]("hello")
'HELLO'

The value of another_dict["first"] is the function print. Therefore, another_dict["first"]("hello") is the same as the function call print("hello").

The key of a dictionary cannot be any data type, though. Have a look at the following examples:

>>> one_more_dictionary = {[1, 2]: "hello"}
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: unhashable type: 'list'

>>> one_more_dictionary = {(1, 2): "hello"}
>>> one_more_dictionary
{(1, 2): 'hello'}

Keys need to be hashable. You can see that when you tried to use a list as a key in the dictionary, you got an ‘unhashable type’ error. However, tuples can be used.

Sets share the same type of bracket with dictionaries, the curly brackets {}, but items within a set are individual items and not pairs:

>>> some_set = {4, 6, 7, 6, 3, 4, 5, 4}
>>> type(some_set)
<class 'set'>
>>> some_set
{3, 4, 5, 6, 7}

Each value in a set must be distinct and therefore can only appear once. In the example above, you can see that the repeated values have been excluded from the set.

Note that when you want to create an empty set, you cannot use the same method as with lists, tuples, and dictionaries since the curly brackets default to an empty dictionary:

>>> a = []
>>> type(a)
<class 'list'>

>>> b = ()
>>> type(b)
<class 'tuple'>

>>> c = {}
>>> type(c)
<class 'dict'>

>>> d = set()
>>> type(d)
<class 'set'>

Before going back to the animation code, we should have a quick word about comprehensions for lists, tuples, dictionaries, and sets.

Comprehensions

When using lists, tuples, dictionaries, and sets in Python, you’ll often need to initialise the empty data structure and then populate it with values. Often, you can use comprehensions to do this:

>>> some_list = [4, 6, 7, 3, 2, 10, 4]
>>> some_list
[4, 6, 100, 3, 2, 10, 4]

>>> new_list = [item * 2 for item in some_list]
>>> new_list
[8, 12, 200, 6, 4, 20, 8]

>>> new_set = {item * 2 for item in some_list}
>>> new_set
{4, 6, 8, 200, 12, 20}

You can use the same method for dictionaries by defining both the key and the value in the comprehension:

>>> names = ["James", "Mary", "Kate"]
>>> numbers = [10, 20, 15]

>>> some_dictionary = {key: value for key, value in zip(names, numbers)}
>>> some_dictionary
{'James': 10, 'Mary': 20, 'Kate': 15}

When using comprehensions to populate tuples, you need to beware of a common error:

>>> some_numbers = (item * 2 for item in some_list)
>>> some_numbers
<generator object <genexpr> at 0x7fe68991b3c0>

>>> some_numbers = tuple(item * 2 for item in some_list)
>>> some_numbers
(8, 12, 14, 6, 4, 20, 8)

The expression in the parentheses () alone returns a generator and not a tuple. You can use the tuple() function with a comprehension expression to create a tuple.

Adding Tiles to the Screen

You’ve created the parameters grid_size and grid_scale earlier that allow you to create a grid and map it to the screen size in pixels. You’ll read more about this mapping between grid and screen later on. You also defined fraction_of_grid_points_used as 0.35 earlier, or 35% of all grid cells. This leads to 89 tiles in this animation. Therefore, you need to select 89 random pairs of grid coordinates which will host the tiles.

Choosing the Tile Coordinates

However, you need to ensure that the program selects 89 unique pairs of grid coordinates. One way of achieving this is by using a set:

import random
import time
import turtle

# Parameters to set up animation
background_colour = 50, 50, 50
screen_size = 800, 800

grid_size = 16, 16
grid_scale = (
    screen_size[0] / grid_size[0],
    screen_size[1] / grid_size[1]
)
fraction_of_grid_points_used = 0.35
n_tiles = int(
    fraction_of_grid_points_used * grid_size[0] * grid_size[1]
)

# Create window
window = turtle.Screen()
window.tracer(0)
window.colormode(255)
window.setup(*screen_size)
window.bgcolor(background_colour)

# Choose grid coordinates that will contain tiles
tile_grid_coords = set()
while len(tile_grid_coords) < n_tiles:
    tile_grid_coords.add(
        (
            random.randint(0, grid_size[0] - 1),
            random.randint(0, grid_size[1] - 1)
        )
    )

# Create balls
balls = []

def create_new_ball():
    ball = turtle.Turtle()
    ball.penup()
    ball.shape("circle")
    ball.pencolor("white")
    ball.setposition(
        random.randint(-screen_size[0] // 2, screen_size[0] // 2),
        random.randint(-screen_size[1] // 2, screen_size[1] // 2)
    )
    ball.setheading(random.randint(0, 359))
    ball.ball_speed = 0.5

    balls.append(ball)

create_new_ball()  # Start animation with one ball

# Main animation loop
while True:
    for ball in balls:
        # Move ball
        ball.forward(ball.ball_speed)
        # If ball goes out of bounds, move to other side
        if abs(ball.xcor()) > screen_size[0] / 2:
            ball.setx(-ball.xcor())
        if abs(ball.ycor()) > screen_size[1] / 2:
            ball.sety(-ball.ycor())

    window.update()
    time.sleep(0.001)

You initialised an empty set and used the uniqueness property of sets to run a while loop until the required number of coordinates are reached. The grid coordinates that the program chooses range from (0, 0) to (15, 15). You can add a call to print(tile_grid_coords) after the loop to display the grid coordinates chosen if you wish.

Tile Colours and Actions

Before you’re ready to draw the tiles, you’ll need to link each tile colour with an action. When a ball hits a tile, it will perform a specific action depending on that tile’s colour.

The three actions that a ball can perform are:

  • Increase speed if the ball hits a green tile
  • Decrease speed if the ball hits a red tile
  • Change direction randomly if the ball hits an orange tile

You can start by defining these three functions, each taking a Turtle object’s name as an input argument. You also define two new parameters to set the maximum speed that a ball can reach, to avoid balls going too fast, and the step size you’d like to use to increase and decrease the ball speed each time it hits a green or red tile:

import random
import time
import turtle

# Parameters to set up animation
background_colour = 50, 50, 50
screen_size = 800, 800

grid_size = 16, 16
grid_scale = (
    screen_size[0] / grid_size[0],
    screen_size[1] / grid_size[1]
)
fraction_of_grid_points_used = 0.35
n_tiles = int(
    fraction_of_grid_points_used * grid_size[0] * grid_size[1]
)

max_ball_speed = 2
ball_speed_step = 0.2

# Create window
window = turtle.Screen()
window.tracer(0)
window.colormode(255)
window.setup(*screen_size)
window.bgcolor(background_colour)

# Choose grid coordinates that will contain tiles
tile_grid_coords = set()
while len(tile_grid_coords) < n_tiles:
    tile_grid_coords.add(
        (
            random.randint(0, grid_size[0] - 1),
            random.randint(0, grid_size[1] - 1)
        )
    )

# Define actions based on grid point colour
def speed_up(ball: turtle.Turtle):
    """Increase ball speed until it reaches max_ball_speed"""
    ball.ball_speed += ball_speed_step
    if ball.ball_speed > max_ball_speed:
        ball.ball_speed = max_ball_speed

def slow_down(ball: turtle.Turtle):
    """Decrease ball speed. Hide and remove from list when stationary"""
    ball.ball_speed -= ball_speed_step
    if ball.ball_speed < ball_speed_step:
        ball.hideturtle()
        balls.remove(ball)

def change_direction(ball: turtle.Turtle):
    """Rotate Turtle object by a random angle in [-90, 90] range"""
    ball.left(random.randint(-90, 90))

# Create balls
balls = []

def create_new_ball():
    ball = turtle.Turtle()
    ball.penup()
    ball.shape("circle")
    ball.pencolor("white")
    ball.setposition(
        random.randint(-screen_size[0] // 2, screen_size[0] // 2),
        random.randint(-screen_size[1] // 2, screen_size[1] // 2)
    )
    ball.setheading(random.randint(0, 359))
    ball.ball_speed = 0.5

    balls.append(ball)

create_new_ball()  # Start animation with one ball

# Main animation loop
while True:
    for ball in balls:
        # Move ball
        ball.forward(ball.ball_speed)
        # If ball goes out of bounds, move to other side
        if abs(ball.xcor()) > screen_size[0] / 2:
            ball.setx(-ball.xcor())
        if abs(ball.ycor()) > screen_size[1] / 2:
            ball.sety(-ball.ycor())

    window.update()
    time.sleep(0.001)

The functions are described in the docstrings for each one. Type hinting is used to improve readability, showing that the input argument should be a Turtle object.

The balls are removed from the list balls when they become stationary, and they cannot exceed the maximum ball speed you set in the parameters at the top of your code.

Mapping Tile Colours to Ball Actions

Your next step is to map the tile colours to each of these actions. Dictionaries are an ideal data structure to create these mappings. As you’ve seen earlier, you can use tuples as keys in a dictionary, and the value can be a function name. You can create a dictionary called actions which maps RGB colour triplets to the function names representing actions:

# ...

# Define actions based on grid point colour
def speed_up(ball: turtle.Turtle):
    """Increase ball speed until it reaches max_ball_speed"""
    ball.ball_speed += ball_speed_step
    if ball.ball_speed > max_ball_speed:
        ball.ball_speed = max_ball_speed

def slow_down(ball: turtle.Turtle):
    """Decrease ball speed. Hide and remove from list when stationary"""
    ball.ball_speed -= ball_speed_step
    if ball.ball_speed < ball_speed_step:
        ball.hideturtle()
        balls.remove(ball)

def change_direction(ball: turtle.Turtle):
    """Rotate Turtle object by a random angle in [-90, 90] range"""
    ball.left(random.randint(-90, 90))

# Map colours to ball actions
actions = {
    (144, 238, 144): speed_up,
    (220, 20, 60): slow_down,
    (255, 127, 80): change_direction,
}

# ...

The tuples used as keys in the dictionary actions represent the light green, red, and orange colours used in this animation. Of course, you can choose your own favourite colours if you wish!

You’re now ready to assign a colour to each tile. You can create another dictionary named tiles which uses the tuples containing the tile coordinates as keys and a colour as a value. This dictionary will contain items in the following format:

{(2, 3): (144, 238, 144), (7, 2): (255, 127, 80), ...}

Each pair of tile coordinates is mapped onto a colour from the three colours available. You can create the dictionary tiles using a dictionary comprehension:

# ...

# Choose grid coordinates that will contain tiles
tile_grid_coords = set()
while len(tile_grid_coords) < n_tiles:
    tile_grid_coords.add(
        (
            random.randint(0, grid_size[0] - 1),
            random.randint(0, grid_size[1] - 1)
        )
    )

# Define actions based on grid point colour
def speed_up(ball: turtle.Turtle):
    """Increase ball speed until it reaches max_ball_speed"""
    ball.ball_speed += ball_speed_step
    if ball.ball_speed > max_ball_speed:
        ball.ball_speed = max_ball_speed

def slow_down(ball: turtle.Turtle):
    """Decrease ball speed. Hide and remove from list when stationary"""
    ball.ball_speed -= ball_speed_step
    if ball.ball_speed < ball_speed_step:
        ball.hideturtle()
        balls.remove(ball)

def change_direction(ball: turtle.Turtle):
    """Rotate Turtle object by a random angle in [-90, 90] range"""
    ball.left(random.randint(-90, 90))

# Map colours to ball actions
actions = {
    (144, 238, 144): speed_up,
    (220, 20, 60): slow_down,
    (255, 127, 80): change_direction,
}

# Create tiles
tiles = {
    coord: random.choice(tuple(actions.keys()))
    for coord in tile_grid_coords
}

# ...

You loop through tile_grid_coords in the dictionary comprehension and place each item as a key in the dictionary. For each key, you choose a random colour as a value. Since the colours available are the keys of the dictionary named actions, you can use actions.keys() as an argument for random.choice() once you convert to a sequence such as a tuple. You can print(tiles) if you wish to display the set of tiles and their colours.

Converting Between Grid Coordinates and Screen Coordinates

You have to deal with two sets of coordinates in this program:

  • The grid coordinates represent the cells in the 16x16 grid. The bottom-left cell is (0, 0), and the top-right cell is (15, 15).
  • The screen coordinates correspond to each pixel on the screen. In the Turtle module, the centre of the screen has the coordinates (0, 0). Therefore, the screen coordinates include negative and positive values to represent all four quadrants of the screen.

The illustration below shows the relationship between grid coordinates and screen coordinates for a 4x4 grid. The grid coordinates are shown using square brackets and the screen coordinates using round brackets in this illustration:

In the code, the grid is 16x16 instead of 4x4. The smaller grid was only used in the drawing to make it easier to illustrate.

You can now write a couple of functions to convert between the two coordinate systems. You can add these helper functions immediately after defining the parameters at the top of the code:

import random
import time
import turtle

# Parameters to set up animation
background_colour = 50, 50, 50
screen_size = 800, 800

grid_size = 16, 16
grid_scale = (
    screen_size[0] / grid_size[0],
    screen_size[1] / grid_size[1]
)
fraction_of_grid_points_used = 0.35
n_tiles = int(
    fraction_of_grid_points_used * grid_size[0] * grid_size[1]
)

max_ball_speed = 2
ball_speed_step = 0.2

# Functions to convert between grid and screen coordinates
def convert_grid_to_screen_coords(grid_coords):
    return (
        grid_coords[0] * grid_scale[0] - screen_size[0]/2 + grid_scale[0]/2,
        grid_coords[1] * grid_scale[1] - screen_size[1]/2 + grid_scale[1]/2,
    )

def convert_screen_to_grid_coords(screen_coords):
    return (
        round(
            (screen_coords[0] - grid_scale[0]/2 + screen_size[0]/2) / grid_scale[0]
        ),
        round(
            (screen_coords[1] - grid_scale[1]/2 + screen_size[1]/2) / grid_scale[1]
        ),
    )

# ...

In the function convert_grid_to_screen_coords(), a pair of grid coordinates such as (3, 1) is converted to the screen coordinates at the centre of the grid cell. The steps in the function are as follows:

  • The input argument is a tuple containing the grid coordinates.
  • The return value is another tuple containing the screen coordinates at the centre of the cell.
  • The grid coordinates are multiplied by the grid_scale first. This is the size of each cell in the grid in pixels. This gives the left-most pixel when index 0 is used in the tuple indexing or the bottom-most pixel when 1 is used.
  • Since the grid coordinates start at the bottom left while the screen coordinates are centred at the middle of the screen, you need to subtract half the width or height of the screen.
  • You now need to add half the grid_scale value to move from the bottom-left pixel of the grid cell to the centre pixel of the cell.

In the function convert_screen_to_grid_coords(), the screen coordinates of any pixel are converted to the grid coordinates of the cell that contains that pixel:

  • The input argument is a tuple containing the screen coordinates of a pixel.
  • The return value is another tuple containing the grid coordinates for the grid which contains the pixel.
  • The calculation is the reverse of the one described for convert_grid_to_screen(). The result is rounded to give the integers needed for the grid coordinate system.

There’s a bit more detail about the transformations occurring in these functions in an appendix to this article.

Drawing the Tiles

It’s time to draw the tiles on the screen. You can create a new Turtle object to draw the tiles and then loop through the dictionary tiles to draw each one.

The keys in tiles are the grid coordinates of the cell, and the values are the colours. The steps needed to draw the tiles are the following:

  • Loop through tiles.items() and assign the keys to the name coord and the values to the name colour.
  • Convert grid coordinates to screen coordinates.
  • Move the Turtle object to the bottom-left region of the cell, allowing for a margin so that tiles are not in contact with each other. The factor of 0.9 is used for this.
  • Change the Turtle object’s colour to the colour associated with the tile, which is stored in the tiles dictionary.
  • Draw a square with the Turtle object. The factor of 0.8 ensures that a margin is left between the tile drawn and the edge of the cell.

You can add this loop to your code:

import random
import time
import turtle

# Parameters to set up animation
background_colour = 50, 50, 50
screen_size = 800, 800

grid_size = 16, 16
grid_scale = (
    screen_size[0] / grid_size[0],
    screen_size[1] / grid_size[1]
)
fraction_of_grid_points_used = 0.35
n_tiles = int(
    fraction_of_grid_points_used * grid_size[0] * grid_size[1]
)

max_ball_speed = 2
ball_speed_step = 0.2

# Functions to convert between grid and screen coordinates
def convert_grid_to_screen_coords(grid_coords):
    return (
        grid_coords[0] * grid_scale[0] - screen_size[0]/2 + grid_scale[0]/2,
        grid_coords[1] * grid_scale[1] - screen_size[1]/2 + grid_scale[1]/2,
    )

def convert_screen_to_grid_coords(screen_coords):
    return (
        round(
            (screen_coords[0] - grid_scale[0]/2 + screen_size[0]/2) / grid_scale[0]
        ),
        round(
            (screen_coords[1] - grid_scale[1]/2 + screen_size[1]/2) / grid_scale[1]
        ),
    )

# Create window
window = turtle.Screen()
window.tracer(0)
window.colormode(255)
window.setup(*screen_size)
window.bgcolor(background_colour)

# Choose grid coordinates that will contain tiles
tile_grid_coords = set()
while len(tile_grid_coords) < n_tiles:
    tile_grid_coords.add(
        (
            random.randint(0, grid_size[0] - 1),
            random.randint(0, grid_size[1] - 1)
        )
    )

# Define actions based on grid point colour
def speed_up(ball: turtle.Turtle):
    """Increase ball speed until it reaches max_ball_speed"""
    ball.ball_speed += ball_speed_step
    if ball.ball_speed > max_ball_speed:
        ball.ball_speed = max_ball_speed

def slow_down(ball: turtle.Turtle):
    """Decrease ball speed. Hide and remove from list when stationary"""
    ball.ball_speed -= ball_speed_step
    if ball.ball_speed < ball_speed_step:
        ball.hideturtle()
        balls.remove(ball)

def change_direction(ball: turtle.Turtle):
    """Rotate Turtle object by a random angle in [-90, 90] range"""
    ball.left(random.randint(-90, 90))

# Map colours to ball actions
actions = {
    (144, 238, 144): speed_up,
    (220, 20, 60): slow_down,
    (255, 127, 80): change_direction,
}

# Create tiles
tiles = {
    coord: random.choice(tuple(actions.keys()))
    for coord in tile_grid_coords
}

# Create balls
balls = []

def create_new_ball():
    ball = turtle.Turtle()
    ball.penup()
    ball.shape("circle")
    ball.pencolor("white")
    ball.setposition(
        random.randint(-screen_size[0] // 2, screen_size[0] // 2),
        random.randint(-screen_size[1] // 2, screen_size[1] // 2)
    )
    ball.setheading(random.randint(0, 359))
    ball.ball_speed = 0.5

    balls.append(ball)

create_new_ball()  # Start animation with one ball

# Draw tiles on screen
grid_draw = turtle.Turtle()
grid_draw.penup()
grid_draw.hideturtle()

for coord, colour in tiles.items():
    coords = convert_grid_to_screen_coords(coord)
    grid_draw.setposition(
        coords[0] - grid_scale[0] / 2 * 0.9,
        coords[1] - grid_scale[1] / 2 * 0.9
    )
    grid_draw.color(colour)
    grid_draw.pendown()
    for _ in range(2):
        grid_draw.forward(grid_scale[0] * 0.8)
        grid_draw.left(90)
        grid_draw.forward(grid_scale[1] * 0.8)
        grid_draw.left(90)
    grid_draw.penup()

# Main animation loop
while True:
    for ball in balls:
        # Move ball
        ball.forward(ball.ball_speed)
        # If ball goes out of bounds, move to other side
        if abs(ball.xcor()) > screen_size[0] / 2:
            ball.setx(-ball.xcor())
        if abs(ball.ycor()) > screen_size[1] / 2:
            ball.sety(-ball.ycor())

    window.update()
    time.sleep(0.001)

When you run this code, you’ll see the single ball moving across the screen over the drawings of the tiles:

Before adding more balls to the animation, you can deal with the interactions between the ball and the tiles it hits.

Creating Interactions Between Balls and Tiles

You only have one ball in the animation so far. However, any steps you take in the main animation loop will apply to all balls in the animation since you’re looping through the list balls.

The steps required to detect when a ball hits a tile and to perform the required actions on the ball are the following:

  • Find which cell in the grid the ball is currently in.
  • Check whether that cell has a tile on it.
  • If the ball is on a tile, find the colour of the tile and what action is associated with that colour.
  • Implement the required action on the ball.

There is another pitfall you’ll need to be careful about. The ball is moving in small steps, and therefore, it will overlap on a single tile for several iterations of the main animation loop. However, you only want the action to be performed when the ball first hits a tile. You can add another instance variable to each ball to store the last tile the ball has hit and then add this as an additional check to determine whether a ball has just hit a tile.

You can add the new instance variable and make additions to the main animation loop:

import random
import time
import turtle

# Parameters to set up animation
background_colour = 50, 50, 50
screen_size = 800, 800

grid_size = 16, 16
grid_scale = (
    screen_size[0] / grid_size[0],
    screen_size[1] / grid_size[1]
)
fraction_of_grid_points_used = 0.35
n_tiles = int(
    fraction_of_grid_points_used * grid_size[0] * grid_size[1]
)

max_ball_speed = 2
ball_speed_step = 0.2

# Functions to convert between grid and screen coordinates
def convert_grid_to_screen_coords(grid_coords):
    return (
        grid_coords[0] * grid_scale[0] - screen_size[0]/2 + grid_scale[0]/2,
        grid_coords[1] * grid_scale[1] - screen_size[1]/2 + grid_scale[1]/2,
    )

def convert_screen_to_grid_coords(screen_coords):
    return (
        round(
            (screen_coords[0] - grid_scale[0]/2 + screen_size[0]/2) / grid_scale[0]
        ),
        round(
            (screen_coords[1] - grid_scale[1]/2 + screen_size[1]/2) / grid_scale[1]
        ),
    )

# Create window
window = turtle.Screen()
window.tracer(0)
window.colormode(255)
window.setup(*screen_size)
window.bgcolor(background_colour)

# Choose grid coordinates that will contain tiles
tile_grid_coords = set()
while len(tile_grid_coords) < n_tiles:
    tile_grid_coords.add(
        (
            random.randint(0, grid_size[0] - 1),
            random.randint(0, grid_size[1] - 1)
        )
    )

# Define actions based on grid point colour
def speed_up(ball: turtle.Turtle):
    """Increase ball speed until it reaches max_ball_speed"""
    ball.ball_speed += ball_speed_step
    if ball.ball_speed > max_ball_speed:
        ball.ball_speed = max_ball_speed

def slow_down(ball: turtle.Turtle):
    """Decrease ball speed. Hide and remove from list when stationary"""
    ball.ball_speed -= ball_speed_step
    if ball.ball_speed < ball_speed_step:
        ball.hideturtle()
        balls.remove(ball)

def change_direction(ball: turtle.Turtle):
    """Rotate Turtle object by a random angle in [-90, 90] range"""
    ball.left(random.randint(-90, 90))

# Map colours to ball actions
actions = {
    (144, 238, 144): speed_up,
    (220, 20, 60): slow_down,
    (255, 127, 80): change_direction,
}

# Create tiles
tiles = {
    coord: random.choice(tuple(actions.keys()))
    for coord in tile_grid_coords
}

# Create balls
balls = []

def create_new_ball():
    ball = turtle.Turtle()
    ball.penup()
    ball.shape("circle")
    ball.pencolor("white")
    ball.setposition(
        random.randint(-screen_size[0] // 2, screen_size[0] // 2),
        random.randint(-screen_size[1] // 2, screen_size[1] // 2)
    )
    ball.setheading(random.randint(0, 359))
    ball.ball_speed = 0.5
    ball.current_grid = None

    balls.append(ball)

create_new_ball()  # Start animation with one ball

# Draw tiles on screen
grid_draw = turtle.Turtle()
grid_draw.penup()
grid_draw.hideturtle()

for coord, colour in tiles.items():
    coords = convert_grid_to_screen_coords(coord)
    grid_draw.setposition(
        coords[0] - grid_scale[0] / 2 * 0.9,
        coords[1] - grid_scale[1] / 2 * 0.9
    )
    grid_draw.color(colour)
    grid_draw.pendown()
    for _ in range(2):
        grid_draw.forward(grid_scale[0] * 0.8)
        grid_draw.left(90)
        grid_draw.forward(grid_scale[1] * 0.8)
        grid_draw.left(90)
    grid_draw.penup()

# Main animation loop
while True:
    for ball in balls:
        # Move ball
        ball.forward(ball.ball_speed)
        # If ball goes out of bounds, move to other side
        if abs(ball.xcor()) > screen_size[0] / 2:
            ball.setx(-ball.xcor())
        if abs(ball.ycor()) > screen_size[1] / 2:
            ball.sety(-ball.ycor())

        # Check whether ball hit tile and perform required action
        ball_grid_coords = convert_screen_to_grid_coords(ball.position())
        if (
                ball_grid_coords in tiles.keys()
                and ball_grid_coords != ball.current_grid
        ):
            colour = tiles[ball_grid_coords]
            actions[colour](ball)
            ball.current_grid = ball_grid_coords

    window.update()
    time.sleep(0.001)

The if statement you just added in the while loop contains two conditions:

  • The ball must be on a tile. You verify this by checking whether the tuple containing the grid coordinates of the ball’s current position is one of the keys in the dictionary tiles.
  • The tile the ball is currently on must not be the same one as in the previous iteration.

When both conditions are met, you perform the following steps:

  • You get the tile’s colour from the dictionary tiles and store it in the variable colour.
  • You get the name of the function mapped to the colour and call the function with ball as its argument. This is the same technique summarised in the section reviewing dictionaries above. actions is a dictionary, and therefore, actions[colour] gives the value associated with the tuple colour. This value is a function name (speed_up, slow_down, or change_direction).
  • You assign the current grid coordinates to the instance variable ball.current_grid so that these actions are not performed in the next iterations if the ball is still on this tile.

The output from the code so far gives the following output:

Note that as the tiles’ positions and colours and the ball’s position and orientation are all random, the outcome of each run will be different. When there’s only one ball, it is possible that this ball will be short-lived if it hits too many red tiles early on!

Using Colour to Show Ball Speed

You’ll indicate the speed of the ball by changing the ball’s colour. To achieve this, you’ll first need to select a colour for the balls. You can add this to the parameters at the top of your code.

Then, you can add a function that works out the right shade of that colour based on the speed of the ball. This function works out what fraction of the maximum speed the ball’s current speed is and scales the red, green, and blue values of the ball’s colour accordingly. You can use fillcolor(), which is another Turtle method, to fill the shape of the ball:

import random
import time
import turtle

# Parameters to set up animation
background_colour = 50, 50, 50
screen_size = 800, 800

grid_size = 16, 16
grid_scale = (
    screen_size[0] / grid_size[0],
    screen_size[1] / grid_size[1]
)
fraction_of_grid_points_used = 0.35
n_tiles = int(
    fraction_of_grid_points_used * grid_size[0] * grid_size[1]
)

ball_colour = 0, 191, 255
max_ball_speed = 2
ball_speed_step = 0.2

# Functions to convert between grid and screen coordinates
def convert_grid_to_screen_coords(grid_coords):
    return (
        grid_coords[0] * grid_scale[0] - screen_size[0]/2 + grid_scale[0]/2,
        grid_coords[1] * grid_scale[1] - screen_size[1]/2 + grid_scale[1]/2,
    )

def convert_screen_to_grid_coords(screen_coords):
    return (
        round(
            (screen_coords[0] - grid_scale[0]/2 + screen_size[0]/2) / grid_scale[0]
        ),
        round(
            (screen_coords[1] - grid_scale[1]/2 + screen_size[1]/2) / grid_scale[1]
        ),
    )

# Create window
window = turtle.Screen()
window.tracer(0)
window.colormode(255)
window.setup(*screen_size)
window.bgcolor(background_colour)

# Choose grid coordinates that will contain tiles
tile_grid_coords = set()
while len(tile_grid_coords) < n_tiles:
    tile_grid_coords.add(
        (
            random.randint(0, grid_size[0] - 1),
            random.randint(0, grid_size[1] - 1)
        )
    )

# Define actions based on grid point colour
def speed_up(ball: turtle.Turtle):
    """Increase ball speed until it reaches max_ball_speed"""
    ball.ball_speed += ball_speed_step
    if ball.ball_speed > max_ball_speed:
        ball.ball_speed = max_ball_speed

def slow_down(ball: turtle.Turtle):
    """Decrease ball speed. Hide and remove from list when stationary"""
    ball.ball_speed -= ball_speed_step
    if ball.ball_speed < ball_speed_step:
        ball.hideturtle()
        balls.remove(ball)

def change_direction(ball: turtle.Turtle):
    """Rotate Turtle object by a random angle in [-90, 90] range"""
    ball.left(random.randint(-90, 90))

# Map colours to ball actions
actions = {
    (144, 238, 144): speed_up,
    (220, 20, 60): slow_down,
    (255, 127, 80): change_direction,
}

# Create tiles
tiles = {
    coord: random.choice(tuple(actions.keys()))
    for coord in tile_grid_coords
}

# Create balls
balls = []

def change_ball_colour(ball):
    fraction_of_max_speed = ball.ball_speed / max_ball_speed
    ball.fillcolor(
        int(ball_colour[0] * fraction_of_max_speed),
        int(ball_colour[1] * fraction_of_max_speed),
        int(ball_colour[2] * fraction_of_max_speed),
    )

def create_new_ball():
    ball = turtle.Turtle()
    ball.penup()
    ball.shape("circle")
    ball.pencolor("white")
    ball.setposition(
        random.randint(-screen_size[0] // 2, screen_size[0] // 2),
        random.randint(-screen_size[1] // 2, screen_size[1] // 2)
    )
    ball.setheading(random.randint(0, 359))
    ball.ball_speed = 0.5
    ball.current_grid = None
    change_ball_colour(ball)

    balls.append(ball)

create_new_ball()  # Start animation with one ball

# Draw tiles on screen
grid_draw = turtle.Turtle()
grid_draw.penup()
grid_draw.hideturtle()

for coord, colour in tiles.items():
    coords = convert_grid_to_screen_coords(coord)
    grid_draw.setposition(
        coords[0] - grid_scale[0] / 2 * 0.9,
        coords[1] - grid_scale[1] / 2 * 0.9
    )
    grid_draw.color(colour)
    grid_draw.pendown()
    for _ in range(2):
        grid_draw.forward(grid_scale[0] * 0.8)
        grid_draw.left(90)
        grid_draw.forward(grid_scale[1] * 0.8)
        grid_draw.left(90)
    grid_draw.penup()

# Main animation loop
while True:
    for ball in balls:
        # Move ball
        ball.forward(ball.ball_speed)
        # If ball goes out of bounds, move to other side
        if abs(ball.xcor()) > screen_size[0] / 2:
            ball.setx(-ball.xcor())
        if abs(ball.ycor()) > screen_size[1] / 2:
            ball.sety(-ball.ycor())

        # Check whether ball hit tile and perform required action
        ball_grid_coords = convert_screen_to_grid_coords(ball.position())
        if (
                ball_grid_coords in tiles.keys()
                and ball_grid_coords != ball.current_grid
        ):
            colour = tiles[ball_grid_coords]
            actions[colour](ball)
            ball.current_grid = ball_grid_coords
            change_ball_colour(ball)

    window.update()
    time.sleep(0.001)

You call change_ball_colour() in the function that creates the balls and in the main animation loop when a ball changes speed. The output of the code now looks like this:

Adding More Balls at Regular Intervals

The last step is to add more balls. You can define a parameter to set the time interval between new balls being created and then set a timer that resets every interval after creating a new ball.

Here’s the final version of the Chaotic Balls animation code:

import random
import time
import turtle

# Parameters to set up animation
background_colour = 50, 50, 50
screen_size = 800, 800

grid_size = 16, 16
grid_scale = (
    screen_size[0] / grid_size[0],
    screen_size[1] / grid_size[1]
)
fraction_of_grid_points_used = 0.35
n_tiles = int(
    fraction_of_grid_points_used * grid_size[0] * grid_size[1]
)

ball_colour = 0, 191, 255
new_ball_interval = 2
max_ball_speed = 2
ball_speed_step = 0.2

# Functions to convert between grid and screen coordinates
def convert_grid_to_screen_coords(grid_coords):
    return (
        grid_coords[0] * grid_scale[0] - screen_size[0]/2 + grid_scale[0]/2,
        grid_coords[1] * grid_scale[1] - screen_size[1]/2 + grid_scale[1]/2,
    )

def convert_screen_to_grid_coords(screen_coords):
    return (
        round(
            (screen_coords[0] - grid_scale[0]/2 + screen_size[0]/2) / grid_scale[0]
        ),
        round(
            (screen_coords[1] - grid_scale[1]/2 + screen_size[1]/2) / grid_scale[1]
        ),
    )

# Create window
window = turtle.Screen()
window.tracer(0)
window.colormode(255)
window.setup(*screen_size)
window.bgcolor(background_colour)

# Choose grid coordinates that will contain tiles
tile_grid_coords = set()
while len(tile_grid_coords) < n_tiles:
    tile_grid_coords.add(
        (
            random.randint(0, grid_size[0] - 1),
            random.randint(0, grid_size[1] - 1)
        )
    )

# Define actions based on grid point colour
def speed_up(ball: turtle.Turtle):
    """Increase ball speed until it reaches max_ball_speed"""
    ball.ball_speed += ball_speed_step
    if ball.ball_speed > max_ball_speed:
        ball.ball_speed = max_ball_speed

def slow_down(ball: turtle.Turtle):
    """Decrease ball speed. Hide and remove from list when stationary"""
    ball.ball_speed -= ball_speed_step
    if ball.ball_speed < ball_speed_step:
        ball.hideturtle()
        balls.remove(ball)

def change_direction(ball: turtle.Turtle):
    """Rotate Turtle object by a random angle in [-90, 90] range"""
    ball.left(random.randint(-90, 90))

# Map colours to ball actions
actions = {
    (144, 238, 144): speed_up,
    (220, 20, 60): slow_down,
    (255, 127, 80): change_direction,
}

# Create tiles
tiles = {
    coord: random.choice(tuple(actions.keys()))
    for coord in tile_grid_coords
}

# Create balls
balls = []

def change_ball_colour(ball):
    fraction_of_max_speed = ball.ball_speed / max_ball_speed
    ball.fillcolor(
        int(ball_colour[0] * fraction_of_max_speed),
        int(ball_colour[1] * fraction_of_max_speed),
        int(ball_colour[2] * fraction_of_max_speed),
    )

def create_new_ball():
    ball = turtle.Turtle()
    ball.penup()
    ball.shape("circle")
    ball.pencolor("white")
    ball.setposition(
        random.randint(-screen_size[0] // 2, screen_size[0] // 2),
        random.randint(-screen_size[1] // 2, screen_size[1] // 2)
    )
    ball.setheading(random.randint(0, 359))
    ball.ball_speed = 0.5
    ball.current_grid = None
    change_ball_colour(ball)

    balls.append(ball)

create_new_ball()  # Start animation with one ball

# Draw tiles on screen
grid_draw = turtle.Turtle()
grid_draw.penup()
grid_draw.hideturtle()

for coord, colour in tiles.items():
    coords = convert_grid_to_screen_coords(coord)
    grid_draw.setposition(
        coords[0] - grid_scale[0] / 2 * 0.9,
        coords[1] - grid_scale[1] / 2 * 0.9
    )
    grid_draw.color(colour)
    grid_draw.pendown()
    for _ in range(2):
        grid_draw.forward(grid_scale[0] * 0.8)
        grid_draw.left(90)
        grid_draw.forward(grid_scale[1] * 0.8)
        grid_draw.left(90)
    grid_draw.penup()

# Main animation loop
start_timer = time.time()
while True:
    # Create new ball every time interval elapses
    if time.time() - start_timer > new_ball_interval:
        create_new_ball()
        start_timer = time.time()

    for ball in balls:
        # Move ball
        ball.forward(ball.ball_speed)
        # If ball goes out of bounds, move to other side
        if abs(ball.xcor()) > screen_size[0] / 2:
            ball.setx(-ball.xcor())
        if abs(ball.ycor()) > screen_size[1] / 2:
            ball.sety(-ball.ycor())

        # Check whether ball hit tile and perform required action
        ball_grid_coords = convert_screen_to_grid_coords(ball.position())
        if (
                ball_grid_coords in tiles.keys()
                and ball_grid_coords != ball.current_grid
        ):
            colour = tiles[ball_grid_coords]
            actions[colour](ball)
            ball.current_grid = ball_grid_coords
            change_ball_colour(ball)

    window.update()
    time.sleep(0.001)

And the output of this code is the following animation:

Final Words

In this article, you used the main built-in data structures in Python in a visual animation including many balls flying around a screen with many coloured tiles. The balls interact with each tile depending on the tile’s colour.

When learning about using lists, tuples, dictionaries, and sets in Python, it’s important to write some simple, short, code snippets to explore these data structures. But there’s also a lot of benefit in using them in a more elaborate manner.

This article and the Chaotic Balls simulation aim to demonstrate an alternative way of using lists, tuples, dictionaries, and sets in Python.

Each of these data structures has its own purposes:

  • You used tuples to store the coordinates and the RGB colours since these don’t need to be flexible containers. Using tuples also allowed you to use them as keys in dictionaries, which you wouldn’t have been able to do if you had used lists.
  • You used a list to store all the balls in the animation. This needs to be a flexible container as the number of balls increases and decreases throughout the animation. You need to store the balls in the same data structure to make use of loops to deal with all the balls in the animation effectively.
  • You used a set when you needed to ensure the pairs of coordinates you created randomly for the tiles were unique.
  • You used dictionaries to store the mappings between several bits of information in your code, including the mapping between tile colours and ball actions, and the mapping between the tile coordinates and their colours.

Now you can run the code and watch the hypnotic movement of the balls for a few minutes. You’ve earned the break!

Further Reading

  • Read more about lists in the Chapter about loops and lists in The Python Coding Book
  • You can also read about linked lists and how they compare with lists in the article about stacks and queues
  • You’ll find out more about dictionaries and tuples, including a word analysis project using dictionaries, in the Chapter about data types in The Python Coding Book
  • The example in this article used instance variables defined directly on instances of the class turtle.Turtle. You can read the article about Python instance variables and the full Chapter about Object-Oriented Programming in The Python Coding Book

Python 3.9 was used for the code in this article


Get the latest blog updates

No spam promise. You’ll get an email when a new blog post is published


Appendix: Converting Between Grid and Screen Coordinates

Let’s look back at the simplified illustration of the screen and grid coordinates:

There are three pixels that have been labelled on this illustration: (0, 0), (20, 20), and (-380, -380). Screen coordinates are shown using round brackets in the diagram.

The grid cells are shown using the dashed lines. There are 16 cells in all as this illustration shows a 4x4 grid. Some of the grid cells are labelled using their grid coordinates. These are shown using square brackets.

There are three transformations needed to convert from one coordinate system to the other. Shifts in coordinate systems are obtained through addition and subtraction. Scaling is obtained through multiplication and division:

  • We are assuming that the grid coordinates represent the centre of a cell. Therefore there’s a shift of half the width of a cell horizontally and half the height of a cell vertically. This is a shift of grid_scale / 2.
  • The origin of the turtle screen is at the centre whereas the grid coordinates have the origin at the bottom left. This leads to another shift of screen_size / 2.
  • Finally, there’s a scaling from an NxN grid to an MxM array of pixels, or vice versa.

Let’s now work through the algorithms in the two functions convert_grid_to_screen_coords() and convert_screen_to_grid_coords() to understand each step. For this example, we’ll use the 4x4 grid set up, for simplicity.

Therefore, the parameters you’ll need are:

  • grid_size = 4, 4
  • screen_size = 800, 800
  • grid_scale = 800/4, 800/4 = 200, 200

convert_grid_to_screen_coords()

Consider the grid cell (2, 2) in the diagram. This is labelled with square brackets in the diagram to avoid confusion with screen coordinates, but I’ll stick to round brackets in the text. The aim of this function is to find the screen coordinates of the pixel at the centre of this cell.

  • The tuple (2, 2) is the argument for the function convert_grid_to_screen_coords().
  • Each number in the tuple is multiplied by grid_scale, which gives (400, 400). This is the scaling needed to go from a 4x4 grid to an 800x800 array. If the screen coordinates also had the origin at the bottom left of the screen, (400, 400) would represent the bottom left corner of the cell.
  • However, in turtle, the screen coordinates (0, 0) represent the centre of the screen. Therefore, you subtract the half-width and half-height of the screen to account for this. This is a shift, or translation, between the two coordinate systems. Since the screen size is 800x800 pixels, this results in the tuple (400-400, 400-400), or more simply (0, 0). The bottom left corner of the cell with grid coordinates (2, 2) is the pixel with screen coordinates (0, 0).
  • However, if you want the pixel at the centre of the cell, you’ll need to account for the size of each cell in pixels. Specifically, you need to add half the size of each cell, that’s grid_scale[i] / 2. i represents either 1 or 2 depending on whether it’s the x- or y-coordinates. The function outputs the tuple (100, 100). This means that the pixel at the centre of grid cell (2, 2) is the pixel with screen coordinates (100, 100).

convert_screen_to_grid_coords()

Consider the pixel with screen coordinates (20, 20) in the illustration. The aim of this function is to find the grid coordinates of the cell that contains this pixel.

  • The tuple (20, 20) is the argument for the function convert_screen_to_grid_coords().
  • When you subtract grid_scale[i] / 2, which is the half-width and half-height of a cell, you get (-80, -80). This shift takes into account the fact that the grid coordinates represent the centre of a cell.
  • Next, you account for the difference in the origin between the grid coordinates and the screen coordinates with another shift. Since you’re converting from screen to grid coordinates, you add the half-width and half-height of the screen. This gives the (-80+400, -80+400) which is (320, 320).
  • Finally, you scale down from an array of pixels to cells in a grid. You achieve this by dividing (320, 320) by grid_scale[i] to give (1.6, 1.6) and round to the nearest integer, giving the output (2, 2). Therefore the pixel (20, 20) lies within the cell with grid coordinates (2, 2).

The post Practise Using Lists, Tuples, Dictionaries, and Sets in Python With the Chaotic Balls Animation appeared first on The Python Coding Book.



from Planet Python
via read more

ItsMyCode: Python ValueError: cannot reindex from a duplicate axis

ItsMyCode |

In Python, you will get a valueerror: cannot reindex from a duplicate axis usually when you set an index to a specific value, reindexing or resampling the DataFrame using reindex method.

If you look at the error message “cannot reindex from a duplicate axis“, it means that Pandas DataFrame has duplicate index values. Hence when we do certain operations such as concatenating a DataFrame, reindexing a DataFrame, or resampling a DataFrame in which the index has duplicate values, it will not work, and Python will throw a ValueError.

Verify if your DataFrame Index contains Duplicate values

When you get this error, the first thing you need to do is to check the DataFrame index for duplicate values using the below code.

df.index.is_unique

The index.is_unique method will return a boolean value. If the index has unique values, it returns True else False.

Test which values in an index is duplicate

If you want to check which values in an index have duplicates, you can use index.duplicated method as shown below.

df.index.duplicated()

The method returns an array of boolean values. The duplicated values are returned as True in an array.

idx = pd.Index(['lama', 'cow', 'lama', 'beetle', 'lama'])
idx.duplicated()

Output

array([False, False,  True, False,  True])

Drop rows with duplicate index values

By using the same index.duplicated method, we can remove the duplicate values in the DataFrame using the following code.

It will traverse the DataFrame from a top-down approach and ensure all the duplicate values in the index are removed, and the unique values are preserved.

df.loc[~df.index.duplicated(), :]

Alternatively, if you use the latest version, you can even use the method df.drop_duplicates() as shown below.

Consider dataset containing ramen rating.

>>> df = pd.DataFrame({
...     'brand': ['Yum Yum', 'Yum Yum', 'Indomie', 'Indomie', 'Indomie'],
...     'style': ['cup', 'cup', 'cup', 'pack', 'pack'],
...     'rating': [4, 4, 3.5, 15, 5]
... })
>>> df
    brand style  rating
0  Yum Yum   cup     4.0
1  Yum Yum   cup     4.0
2  Indomie   cup     3.5
3  Indomie  pack    15.0
4  Indomie  pack     5.0

By default, it removes duplicate rows based on all columns.

>>> df.drop_duplicates()
    brand style  rating
0  Yum Yum   cup     4.0
2  Indomie   cup     3.5
3  Indomie  pack    15.0
4  Indomie  pack     5.0

To remove duplicates on specific column(s), use subset.

>>> df.drop_duplicates(subset=['brand'])
    brand style  rating
0  Yum Yum   cup     4.0
2  Indomie   cup     3.5

To remove duplicates and keep last occurrences, use keep.

>>> df.drop_duplicates(subset=['brand', 'style'], keep='last')
    brand style  rating
1  Yum Yum   cup     4.0
2  Indomie   cup     3.5
4  Indomie  pack     5.0

Prevent duplicate values in a DataFrame index

If you want to ensure Pandas DataFrame without duplicate values in the index, one can set a flag. Setting the allows_duplicate_labels flag to False will prevent the assignment of duplicate values.

df.flags.allows_duplicate_labels = False

Applying this flag to a DataFrame with duplicate values or assigning duplicate values will result in DuplicateLabelError: Index has duplicates.

Overwrite DataFrame index with a new one

Alternatively, to overwrite your current DataFrame index with a new one:

df.index = new_index

or, use .reset_index:

df.reset_index(level=0, inplace=True)

Remove inplace=True if you want it to return the dataframe.

The post Python ValueError: cannot reindex from a duplicate axis appeared first on ItsMyCode.



from Planet Python
via read more

Saturday, October 30, 2021

Podcast.__init__: Build Composable And Reusable Feature Engineering Pipelines with Feature-Engine

Every machine learning model has to start with feature engineering. This is the process of combining input variables into a more meaningful signal for the problem that you are trying to solve. Many times this process can lead to duplicating code from previous projects, or introducing technical debt in the form of poorly maintained feature pipelines. In order to make the practice more manageable Soledad Galli created the feature-engine library. In this episode she explains how it has helped her and others build reusable transformations that can be applied in a composable manner with your scikit-learn projects. She also discusses the importance of understanding the data that you are working with and the domain in which your model will be used to ensure that you are selecting the right features.

Summary

Every machine learning model has to start with feature engineering. This is the process of combining input variables into a more meaningful signal for the problem that you are trying to solve. Many times this process can lead to duplicating code from previous projects, or introducing technical debt in the form of poorly maintained feature pipelines. In order to make the practice more manageable Soledad Galli created the feature-engine library. In this episode she explains how it has helped her and others build reusable transformations that can be applied in a composable manner with your scikit-learn projects. She also discusses the importance of understanding the data that you are working with and the domain in which your model will be used to ensure that you are selecting the right features.

Announcements

  • Hello and welcome to Podcast.__init__, the podcast about Python’s role in data and science.
  • When you’re ready to launch your next app or want to try a project you hear about on the show, you’ll need somewhere to deploy it, so take a look at our friends over at Linode. With the launch of their managed Kubernetes platform it’s easy to get started with the next generation of deployment and scaling, powered by the battle tested Linode platform, including simple pricing, node balancers, 40Gbit networking, dedicated CPU and GPU instances, and worldwide data centers. Go to pythonpodcast.com/linode and get a $100 credit to try out a Kubernetes cluster of your own. And don’t forget to thank them for their continued support of this show!
  • Your host as usual is Tobias Macey and today I’m interviewing Soledad Galli about feature-engine, a Python library to engineer features for use in machine learning models

Interview

  • Introductions
  • How did you get introduced to Python?
  • Can you describe what feature-engine is and the story behind it?
  • What are the complexities that are inherent to feature engineering?
    • What are the problems that are introduced due to incidental complexity and technical debt?
  • What was missing in the available set of libraries/frameworks/toolkits for feature engineering that you are solving for with feature-engine?
  • What are some examples of the types of domain knowledge that are needed to effectively build features for an ML model?
  • Given the fact that features are constructed through methods such as normalizing data distributions, imputing missing values, combining attributes, etc. what are some of the potential risks that are introduced by incorrectly applied transformations or invalid assumptions about the impact of these manipulations?
  • Can you describe how feature-engine is implemented?
    • How have the design and goals of the project changed or evolved since you started working on it?
  • What (if any) difference exists in the feature engineering process for frameworks like scikit-learn as compared to deep learning approaches using PyTorch, Tensorflow, etc.?
  • Can you describe the workflow of identifying and generating useful features during model development?
    • What are the tools that are available for testing and debugging of the feature pipelines?
  • What do you see as the potential benefits or drawbacks of integrating feature-engine with a feature store such as Feast or Tecton?
  • What are the most interesting, innovative, or unexpected ways that you have seen feature-engine used?
  • What are the most interesting, unexpected, or challenging lessons that you have learned while working on feature-engine?
  • When is feature-engine the wrong choice?
  • What do you have planned for the future of feature-engine?

Keep In Touch

Picks

Closing Announcements

  • Thank you for listening! Don’t forget to check out our other show, the Data Engineering Podcast for the latest on modern data management.
  • Visit the site to subscribe to the show, sign up for the mailing list, and read the show notes.
  • If you’ve learned something or tried out a project from the show then tell us about it! Email hosts@podcastinit.com) with your story.
  • To help other people find the show please leave a review on iTunes and tell your friends and co-workers

Links

The intro and outro music is from Requiem for a Fish The Freak Fandango Orchestra / CC BY-SA



from Planet Python
via read more

Codementor: Django Website Template - Material Kit Design

Open-source Django Website template crafted on top of a pixel-perfect Bootstrap 5 design: Material Kit (free version).

from Planet Python
via read more

Weekly Python StackOverflow Report: (ccxcix) stackoverflow python report

These are the ten most rated questions at Stack Overflow last week.
Between brackets: [question score / answers count]
Build date: 2021-10-30 13:46:37 GMT


  1. NumPy: construct squares along diagonal of matrix / expand diagonal matrix - [15/3]
  2. Convert subset of columns to rows by combining columns - [14/3]
  3. Efficient algorithm to get all the combinations of numbers that are within a certain range from 2 lists in python - [8/2]
  4. Is the key order the same for OrderedDict and dict? - [6/3]
  5. Django REST API accept list instead of dictionary in post request - [6/2]
  6. cannot update spyder=5.1.5 on new anaconda install - [6/1]
  7. Why does starred assignment produce lists and not tuples? - [6/1]
  8. Is there a way to match inequalities in Python ≥ 3.10? - [5/1]
  9. Efficient way of using numpy memmap when training neural network with pytorch - [5/0]
  10. How to find which column contains a certain value? - [4/4]


from Planet Python
via read more

ItsMyCode: Python JSONPath

ItsMyCode |

JSONPath is an expression language that is used to parse the JSON data in Python. JSONPath is similar to XPath in XML, where we parse the XML data. 

JSONPath provides a simpler syntax to query JSON data and get the desired value in Python. Using JSONPath will be the more efficient way to parse and query JSON data as we don’t have to load the entire JSON data. This approach is more memory-optimized compared to any other way of querying JSON.

JSONPath Library in Python

There are many JSONPath libraries for Python, and the most popular one is the jsonpath-ng library. It’s written in the native Python language and supports both Python 2 and Python 3 versions.

jsonpath-ng is the final implementation of JSONPath for Python that aims for standard-compliant including arithmetic and binary comparison operators s, as defined in the original JSONPath proposal.  

This packages merges both jsonpath-rw and jsonpath-rw-ext and provides several AST API enhancements, such as the ability to update or remove nodes in the tree.

Installing jsonpath-ng Module

To install jsonpath-ng library, use the below pip install command. 

pip install --upgrade jsonpath-ng

The above command will install the latest version of the jsonpath-ng library on your machine. Once installed, you can import in the Python IDE using the below code.

import jsonpath_ng

Jsonpath operators:

Below are the list of operators you can use for getting json data values.

Syntax Meaning
jsonpath1 . jsonpath2 All nodes matched by jsonpath2 starting at any node matching jsonpath1
jsonpath [ whatever ] Same as jsonpath.whatever
jsonpath1 .. jsonpath2 All nodes matched by jsonpath2 that descend from any node matching jsonpath1
jsonpath1 where jsonpath2 Any nodes matching jsonpath1 with a child matching jsonpath2
jsonpath1 | jsonpath2 Any nodes matching the union of jsonpath1 and jsonpath2

Parsing a Simple JSON Data using JSONPath

A Simple example of parsing the JSON and fetching the JSON value using the attribute key.

# Program to parse JSON data in Python 
import json
from jsonpath_ng import jsonpath, parse

employee_data = '{"id":1, "first_name":"Chandler" , "last_name":"Bing"}'
json_data = json.loads(employee_data)

jsonpath_expr= parse('$.first_name')
first_name = jsonpath_expr.find(json_data)

print("The First Name of the employee is: ", first_name[0].value)

Output

The First Name of the employee is  Chandler

Parsing a Json Array using JSONPath Expression

The JSON key contains the list of values and uses the JSON Path expression. We can parse and query the exact field values of the JSON.

{
    "books": [
        {
            "category": "reference",
            "author": "Nigel Rees",
            "title": "Sayings of the Century",
            "isbn": "6-246-2356-8",
            "price": 8.95
        },
        {
            "category": "fiction",
            "author": "Evelyn Waugh",
            "title": "Sword of Honour",
            "isbn": "5-444-34234-8",
            "price": 12.99
        },
        {
            "category": "fiction",
            "author": "Herman Melville",
            "title": "Moby Dick",
            "isbn": "0-553-21311-3",
            "price": 8.99
        },
        {
            "category": "fiction",
            "author": "J. R. R. Tolkien",
            "title": "The Lord of the Rings",
            "isbn": "0-395-19395-8",
            "price": 22.99
        }
    ]
}

In the above JSON data, if we need the list of all ISBN of the book, we can use the below code to get the data using JSONPath expression as shown below.

# Program to parse JSON data in Python 
import json
from jsonpath_ng import jsonpath, parse

with open("books.json", 'r') as json_file:
    json_data = json.load(json_file)

jsonpath_expression = parse('books[*].isbn')

for match in jsonpath_expression.find(json_data):
    print(f'Books ISBN: {match.value}')


Output

Books ISBN: 6-246-2356-8
Books ISBN: 5-444-34234-8
Books ISBN: 0-553-21311-3
Books ISBN: 0-395-19395-8

The post Python JSONPath appeared first on ItsMyCode.



from Planet Python
via read more

TestDriven.io: Working with Static and Media Files in Django

This article looks at how to work with static and media files in a Django project, locally and in production. from Planet Python via read...