How to Visualize Data in Python — Graphviz, ImageDraw, and Turtle

  • “Numbers have an important story to tell. They rely on you to give them a clear and convincing voice.”Stephen Few
  • “Visualizations act as a campfire around which we gather to tell stories.”Al Shalloway
  • “The goal is to turn data into information and information into insight.”Carly Fiorina

These quotes are from  Data Visualization experts showing the benefits of Visualizing Data. This short article will discover how to visualize data into a diagram using Python.

You will educate yourself about the importance of data visualization at the start, then examine the three powerful Python libraries to employ for drawing a diagram, and finally dive into an example on financial data visualization. 

Visualization Explained 

Extracting information from the raw data is challenging, and also you can’t identify patterns or trends. Our brain makes us understand pictures much faster than numbers.  We can create graphs and diagrams to understand the data much better. We can find a story hidden under your raw data with powerful graphical tools.  You can understand what this means by an example.

Suppose you started a candy bar manufacturing unit last year and sold various energy bars for the year 2021. In H1 of the year 2021, you have introduced new products, Cinnamon Energy Bar and Berry Energy Bar.

The table below shows the products sales data for 2021 to analyze the performance.

Image 1. 

From the data given above, you can see that Choco Energy Bar sold more than other candy bars, i.e., worth USD 1,174,905. The sales were low for Berry Energy Bar, i.e., USD 181,804.

But with this information is not enough to decide products performance. We can’t identify any trends from the given table of data. You can see the patterns or trends with a graphical tool like a histogram chart.

Image 2. 

Image 2 shows the graph from the above data. In this graph, the x-axis is Month, and the y-axis shows the percentage of total sales. The products represent in different colors. You can see that the Green color indicates Berry Energy Bar (BEB), the Yellow color represents Cinnamon Energy Bar (CEB), and likewise. Refer to the Legend above the bars. 

You can extract the following information from this graph.

  • During the months between Jan – Mar, as Choco Energy Bar (HEB) sales increase, the Apple Energy Bar (AEB) decreases. Both products are inversely related.
  • The introduction of CEB in June has led HEB sales to reduce.
  • Also, we can see sales of CEB reduce the other two products.
  • The sales were high for HEB.
  • The sales were low for  AEB.

From the graph, you can see that we can derive more information such as above, which helps to understand the data and derive patterns easier than raw data.

The above example is a hypothetical case. But in real-time, we can give reasoning behind these patterns. Now let us look at how to visualize qualitative data. 

User Requirement

We will learn about the OSEMN data science process.

  • O stands for Obtain. The first process is to extract data from various sources such as Databases, the internet, or surveys.
  • S stands for Scrub. Next, data must be cleaned from error and deal with missing values.
  • E stands for Explore. After the data cleansing, analyze the data using statistics and visualization tools. 
  • M stands for Model. Create Machine Learning models for forecasting and predicting the data.
  • N Stands for INterpret. Finally, draw a meaningful conclusion based on the presented data to make decisions.

Now we have to create this Process into a Diagram that makes others understand well and brings clarity.  

It is possible to create diagrams using the powerful language Python. We will show you three techniques to draw a diagram.

Technique 1: Graphviz

In this First Technique, employ the Graphviz Python module to create Diagrams. Let’s learn about Graphviz before starting coding.

AT&T Labs Research team discovered this powerful package Graphviz. Graphviz is an open-source graphical visualization software to draw directed and undirected graphs. This software was created with the DOT language script and has filename extension “gv”.

Graphviz has different polygon-based shapes, such as box, polygon, ellipse, etc.  Also, numerous Arrow shapes are available to draw a diagram. You can save the graphs in the following formats: BMP, JPEG, JSON, PDF, PNG. The industry, such as Software engineering, Database, web design, machine learning, and others, use the Graphiz software widely.

Now let’s begin coding.

Firstly install the Graphviz module with the following command in the terminal :

brew install graphviz
pip install graphviz

After installing Graphviz, you have to import the Graphviz module as below.

import graphviz

You can start drawing a diagram by creating a Digraph object. The Digraph is a directed graph that stores node and edges, and the source code is in the DoT programming language.

graph = graphviz.Digraph('OSEMN', comment='Data Science Process - OSEMN FrameWork')

The above code creates an ‘OSEMN’ graph using a Diagraph object. The comment displays when you run digraph source code.

Next, create a circle shape node to show data science process headings.

graph.node('o', 'OBTAIN',height="3",fontsize="35pt")
graph.node('s', 'SCRUB',height="3",fontsize="35pt")
graph.node('e', 'EXPLORE',height="3",fontsize="35pt")
graph.node('m', 'MODEL',height="3",fontsize="35pt")
graph.node('i', 'INTERPRET',height="3",fontsize="35pt")

The above code adds the node to the graph object using the node() method. 

The ‘o’ node label is for ‘Obtain’ with a height of 1.5 cm and a font size of 35 points. 

The next node is ‘s’ with the label ‘Scrub’ with a height of 1.5 cm and font size of 35 points.  Node for ‘e,’ ‘m’, and ‘i’ are labeled. There are attributes such as fillcolor, fontname, fontsize, image, imagepos, imagescale, and more.

Now create nodes for a short explanation of the data process headings.

graph.node('1', 'Extract Data from Database,Internet & Survey', fontsize="30pt",height="2")
graph.node('2', 'Clean the Data from error & handle missing data',fontsize="30pt",height="2")
graph.node('3', 'Analyse the Data using descriptive statistics and data visualizations',fontsize="30pt", height="2")
graph.node('4', 'Construct ML Models to predict and forecast',fontsize="30pt",height="2")
graph.node('5', 'Draw Meaningful conclution from the presented data',fontsize="30pt",height="2")

Create a node in numbers for each explanation. “1” is for ‘Extract Data from Database, Internet & Survey.’ “2” is for ‘Clean the Data from error & handle missing data’ and so on. Also, you can set different attributes as well.

After this, you need to connect the nodes Headings and Explanation. The edge method can connect between the nodes.

graph.edge('o','s',constraint = 'false', arrowhead = 'diamond', arrowsize="2")
graph.edge('s','e',constraint = 'false', arrowhead = 'diamond',arrowsize="2")
graph.edge('e','m',constraint = 'false', arrowhead = 'diamond',arrowsize="2")
graph.edge('m','i',constraint = 'false', arrowhead = 'diamond',arrowsize="2")

First, connect the OSEM process through the edge method. Connect the edge between o and s, then s and e, and so on.

The constraint =‘false’ attribute makes the node connected in the horizontal direction. The default direction of the edge is vertical.

The arrowhead’s shape is diamond, and its size is 2, as shown above in the code. There are different arrowhead shapes such as box, crow, curve, dot, inv, and more.

Attributes for edges are also available like arrowhead, arrowsize, arrowtail, color, decorate, and more.

graph.edges(['o1','s2','e3','m4','i5'])

The code above connects edges o and 1, s and 2. We use the edges method to join more nodes.  

print(graph.source)

This command prints the source code in DOT language. Refer to image 4.

graph.render(directory='doctest-output', view=True)

Code will save the diagram output in the directory ‘’doctest-output‘. The view=True attribute automatically opens the pdf file without searching for it. Refer to image 3.

Output:

Image 3. 

Image 4. 

Technique 2: Pillow ImageDraw

Technique 2 employs ImageDraw from the pillow module to draw a diagram. Let’s learn about Pillow.

💡 Alex Clark and Contributors are the ones who produce the Pillow Library from the PIL module. The PIL is the Python Imaging Library for creating and manipulating images produced by Fredrik Lundh and Contributors. The programmer uses PIL for advanced image processing such as editing images, creating new images, creating thumbnails, etc. But the PIL was discontinued in 2011 and replaced Pillow for future usage. It supports all the image formats such as BMP, PNG, JPEG, and TIFF.

ImageDraw module generates 2D Graphics from the image.  It creates an object to draw in a given image. In other words, it makes a drawing board and inserts an image into the board for further editing.

ImageDraw has drawing methods such as 

  • ImageDraw.rectangle – to draw a rectangle on the image
  • ImageDraw.ellipse – to draw a circle on image
  • ImageDraw.line – to draw a line on the image

Now let’s begin coding.

Install the pillow library as below:

pip install Pillow

Next, import the pillow library of Image, ImageDraw and ImageFont modules.

from PIL import Image, ImageDraw, ImageFont

Before moving on, let’s examine the syntax for the ImageDraw module.

PIL.ImageDraw.Draw(im, mode=None)
  • PIL is the Pillow Module.
  • ImageDraw.Draw method is employed to draw an object in the image.
  • im is the image to edit.
  • mode is for the RGB color of the image. 

After Import, you have to create a new image for drawing the OSEMN diagram.

im = Image.new('RGB', (1750, 520), (255, 255, 255))

The Image. new creates a new image with 1750 x 520 pixels and white color. The RGB color for white is 255, 255,255 and stored in the variable im.

Employ ImageDraw method as below:

draw = ImageDraw.Draw(im)

Draw five ovals shape for the data process headings.

draw.ellipse((50, 120, 300, 200), fill=(255, 255, 255), outline=(0, 0, 0))
draw.ellipse((400, 120, 650, 200), fill=(255, 255, 255), outline=(0, 0, 0))
draw.ellipse((750, 120, 1000, 200), fill=(255, 255, 255), outline=(0, 0, 0))
draw.ellipse((1100, 120, 1350, 200), fill=(255, 255, 255), outline=(0, 0, 0))
draw.ellipse((1450, 120, 1700, 200), fill=(255, 255, 255), outline=(0, 0, 0))

draw.ellipse method is to draw a circle object.

The four numbers in a tuple are the location of the object. The x0 (50) is the starting point of the location in a horizontal direction; then, the ending point is x1(300). The y0 (120) is the starting point of the location in a vertical direction, and then the ending point is y1(200). Refer to the image for a clear understanding.

The attribute ‘fill’ fills the color inside the object, i.e., the circle here is white. The value is to be given by RGB code.

The outline is the color for the object’s border, which is black, and the RGB of it is 0,0,0.

Similarly, you have to do for the other circle object with respective locations as shown in through code above.

Next, we need arrows to show the OSEM process in sequence. In the ImageDraw module, there is no arrow Shape object created like a rectangle or circle. So ImageDraw.line method is employed for drawing arrows.

draw.line((301, 160, 399, 160), fill=(0, 0, 0), width=2) # Code 1
draw.line((393, 150, 399, 160), fill=(0, 0, 0), width=2) # Code 2
draw.line((395, 170, 401, 160), fill=(0, 0, 0), width=2) # Code 3
draw.line((651, 160, 749, 160), fill=(0, 0, 0), width=2)
draw.line((743, 150, 749, 160), fill=(0, 0, 0), width=2)
draw.line((745, 170, 751, 160), fill=(0, 0, 0), width=2)
draw.line((1001, 160, 1099, 160), fill=(0, 0, 0), width=2)
draw.line((1093, 150, 1099, 160), fill=(0, 0, 0), width=2)
draw.line((1095, 170, 1101, 160), fill=(0, 0, 0), width=2)
draw.line((1351, 160, 1449, 160), fill=(0, 0, 0), width=2)
draw.line((1443, 150, 1449, 160), fill=(0, 0, 0), width=2)
draw.line((1445, 170, 1451, 160), fill=(0, 0, 0), width=2)

To create a single arrow you must write three lines of code as shown above. 

  • The first code is for a straight line. 
  • The second code is for the cross line below. 
  • The third code is for the cross line above. 

Do these steps for other arrows too.

Create text for each box with ImageDraw.Draw.text method. But font type attribute is mandatory in this method. Identify the path where is your desired font in the system.

size = ImageFont.truetype("/Users/mohamedthoufeeq/Downloads/bebas-neue/BebasNeue-Regular.ttf",35)

Specify the font path and size in ImageFont.truetype methods, then store it into variable size.

draw.text((130, 140),"OBTAIN",(0, 0, 0),font=size)
draw.text((480, 140),"SCRUB",(0, 0, 0),font=size)
draw.text((830, 140),"EXPLORE",(0, 0, 0),font=size)
draw.text((1180, 140),"MODEL",(0, 0, 0),font=size)
draw.text((1520, 140),"INTERPRET",(0, 0, 0),font=size)

From the text method, You can specify the location of the text of Data Process Heading with x and y coordinates as shown in the code.

Next, we mention the headings with double-quote marks. The color of the font is black with RGB code 0,0,0. Mention the variable size in the font attribute. 

Likewise, you can create a diagram for explanation of the headings as per the code below: 

draw.text((1520, 140),"INTERPRET",(0, 0, 0),font=size)
draw.line((175, 200, 175, 300), fill=(0, 0, 0), width=5)
draw.line((525, 200, 525, 300), fill=(0, 0, 0), width=5)
draw.line((875, 200, 875, 300), fill=(0, 0, 0), width=5)
draw.line((1225, 200, 1225, 300), fill=(0, 0, 0), width=5)
draw.line((1575, 200, 1575, 300), fill=(0, 0, 0), width=5)
draw.rectangle((50, 300, 300, 400), fill=(255, 255, 255), outline=(0, 0, 0))
draw.rectangle((400, 300, 650, 400), fill=(255, 255, 255), outline=(0, 0, 0))
draw.rectangle((750, 300, 1000, 400), fill=(255, 255, 255), outline=(0, 0, 0))
draw.rectangle((1100, 300, 1350, 400), fill=(255, 255, 255), outline=(0, 0, 0))
draw.rectangle((1450, 300, 1700, 400), fill=(255, 255, 255), outline=(0, 0, 0))




esize = ImageFont.truetype("/Users/mohamedthoufeeq/Downloads/bebas-neue/BebasNeue-Regular.ttf",20)
draw.text((60, 320),"Extract Data from Database,\nInternet & Survey",(0, 0, 0),font=esize)
draw.text((410, 320),"Clean the Data from error\n& handle missing data",(0, 0, 0),font=esize)
draw.text((790, 320),"Analyse the Data using\ndescriptive statistics\nand data visualizations",(0, 0, 0),font=esize)
draw.text((1110, 320),"Construct ML Models to\n predict and forecast",(0, 0, 0),font=esize)
draw.text((1460, 320),"Draw Meaningful conclusion\n from the presented data",(0, 0, 0),font=esize)

The explanations of each heading are in a rectangle-shaped diagram. Refer to above code.

Finally, save the output in a jpg file with 110% quality through the save function. Refer to the code below:

im.save('pillow_imagedraw.jpg', quality=110)

Output:

Image 5. 

Technique 3:  Turtle

Turtle is another technique employed to draw a diagram. Let’s learn about it.

The turtle module is most suitable for beginners to Python.  This Python library enables users to create shapes, diagrams, and pictures from virtual drawing boards.

💡 Info: The Turtle was initially developed with Logo programming Language by Wally Feurzeig, Seymour Papert, and Cynthia Solomon. This popular graphical package was designed for kids to introduce programming. It gives a great resource to learn python programming. Don’t get discouraged! Adults can use turtle also. As we mentioned above, the module helps new programmers to get a feel for the python programming language in a fun and interactive way. 

On the virtual drawing board, you find a small triangle-shaped symbol which is a pen for drawing, and this pen is called Turtle.

Let’s start coding:

First, install the Turtle module.

pip install PythonTurtle 

Import the turtle module as below:

import turtle

The following code opens a new Python turtle window with a turtle icon:

turtle.Screen()

Set the turtle speed of 5 seconds and pen size of  1 pixel.

turtle.speed(5)
turtle.pensize(1)

Use the virtual pen to draw a diagram on the Python turtle window.

Before moving further to code, let us understand the turtle’s position.

Image 6. 

The screen has four divisions with x and y coordinates. The current location of the turtle is in (0,0) position. So to move to the top left, the coordinates have to be negative number x and positive number y, as shown in DIV 2. Refer to image 6 for positioning the turtle.

Initially, we will show you how to draw an oval, arrow, line, and rectangle shape, then show how to insert headings and explanations into the shapes.

Draw oval shapes for data process heading with code below:

pos = -300   # Code A
for n in range(5):  # Code B
    turtle.up() # Code C
    turtle.goto(pos,100) # Code D
    turtle.down() # Code E
    turtle.seth(-45) # Code F
    turtle.circle(50,90) # Code G
    turtle.circle(25,90) # Code H
    turtle.circle(50,90) # Code I
    turtle.circle(25,90) # Code J
    turtle.up() # Code K
    pos = pos + 120 # Code L
  • Code A: Set the position as -300 for x coordinate and store it with variable pos.
  • Code B:  Create for loop to draw five oval-shaped diagrams.
  • Code C:  The virtual pen was down when we created the window.  So raise the pen up using up() function.
  • Code D: Make the turtle(pen) move to position -300x and 100y to top left using the goto() function on the window.
  • Code E:  Put the virtual pen on the window with the down() function.
  • Code F:  Set the direction of the turtle(pen) to -45 angle using seth() or setheading() function.
  • Code G,H,I,J:  Draw oval shape diagram using circle() function with radius 50, 25 and extent 90. With circle radius 50 and extent 90  draws a half-circle.
  • Code K: Raise the virtual pen up to  stop drawing
  • Code L: Add position by 120 x for drawing next oval shape diagram.

After this draw arrow shape connecting between the oval shape.

pos =-224 # Code M
for n in range(4): # Code N
    turtle.pensize(2) # Code O
    turtle.up() # Code P
    turtle.goto(pos,120) # Code Q
    turtle.down() # Code R
    turtle.seth(-45)  # Code S
    turtle.left(45) # Code T
    turtle.forward(41) # Code U
    turtle.left(150) # Code V
    turtle.forward(10) # Code W
    turtle.right(180) # Code X
    turtle.forward(10) # Code Y
    turtle.left(250) # Code Z
    turtle.forward(10) # Code AA
    turtle.up() # Code  BB
    pos = pos+120 # Code CC
  • Code M:  Set the position as -224 for x coordinate and store it with variable pos for drawing arrows.
  • Code N:  Create for loop to draw four arrows.
  • Code O:  Increase the pen size to 2.
  • Code P:  Raise the pen up.
  • Code Q: Move the pen to the position at -224 x and 120 y coordinates.
  • Code R: Lower the pen down.
  • Code S: Set the pen direction to a -45 degree angle.
  • Code T: Rotate the pen leftwards to a 45-degree angle anti-clockwise.
  • Code U: Move the pen forward with a distance of 41 x from the present position using the forward function. The position is -224 + 41  =  -183 x. It draws a straight line from the middle of the oval shape.
  • Code V: Rotate the pen to the left with a 150-degree angle for drawing above the cross line for the arrow.
  • Code W: Move the pen forward with a distance of 10x from the present position. 
  • Code X: Rotate the pen to the right of 180 degrees clockwise to return to the current position.
  • Code Y: Move the pen forward with a distance of 10x from the present position.
  • Code Z:  Rotate the pen to the left with 250 degrees for drawing below the cross line for the arrow.
  • Code AA: Move the pen forward with a distance of 10x from the present position.
  • Code BB: Raise the pen up.
  • Code CC: Add the position by 120 to draw the next arrow.

Now with the following code, draw five straight lines from the oval.

pos = -265
for n in range(5):
    turtle.pensize(2)
    turtle.up()
    turtle.goto(pos,85)
    turtle.down()
    turtle.seth(220)
    turtle.left(50)
    turtle.forward(30)
    pos = pos+120

Draw 5 Rectangle shape diagrams for explanation of Headings with below code:

pos = -220 # Code DD
for n in range(5): # Code EE
    turtle.pensize(1) # Code FF
    turtle.penup() # Code GG
    turtle.goto(pos,55) # Code HH
    turtle.pendown() # Code II
    turtle.color('black') # Code JJ
    # draw sides for the rectangle
    for side in range(2): # Code KK
        turtle.forward(50)# Code LL
        turtle.right(90) # Code MM
        turtle.forward(100)# Code NN
        turtle.right(90) # Code OO
    pos = pos+120   # Code PP
  • Code HH: Move the pen downwards to draw a rectangle.
  • Code LL to Code OO: Draws two sides of the rectangle.
  • Code KK: Create for loops to draw four sides of the rectangle.

Next let us write the text for Headings and explanation with below code:

turtle.up()
turtle.goto(-290,100)
turtle.write("OBTAIN", font=("Verdana",12, "normal"))
turtle.up()
turtle.goto(-170,100)
turtle.write("SCRUB", font=("Verdana",12, "normal"))
turtle.up()
turtle.goto(-50,100)
turtle.write("EXPLORE", font=("Verdana",12, "normal"))
turtle.up()
turtle.goto(70,100)
turtle.write("MODEL", font=("Verdana",12, "normal"))
turtle.up()
turtle.goto(190,100)
turtle.write("INTERPRET", font=("Verdana",12, "normal"))
turtle.up()
turtle.goto(-305,20)
turtle.write("Extract Data from\n Database,\nInternet & Survey", font=("Verdana",8, "normal"))
turtle.up()
turtle.goto(-195,20)
turtle.write("Clean the Data from \nerror & handle \nmissing data", font=("Verdana",8, "normal"))
turtle.up()
turtle.goto(-75,20)
turtle.write("Analyse the Data using\ndescriptive statistics\nand data visualizations", font=("Verdana",8, "normal"))
turtle.up()
turtle.goto(45,20)
turtle.write("Construct ML Models to\n predict and forecast", font=("Verdana",8, "normal"))
turtle.up()
turtle.goto(165,20)
turtle.write("Draw Meaningful \nconclusion from the \npresented data", font=("Verdana",8, "normal"))
turtle.up()

Hide the turtle symbol or pen with the following command.

turtle.ht()

Use turtle.write a function to write the text in the desired locations with the pen.

You are not required to lower the pen because turtle.write makes pen down for writing text.

Here write the Data process heading and Explanation.

Finally, see the output:

Image 7. 

Bonus: Visualize Financial Data of Apple.com

Let us draw the diagram for Financial Statement for Apple during the Fourth Quarter of 2021

Image 8. 

The above Financial Statements make it harder for you to grasp the essential indicators of the company. So we will draw diagrams to understand the company’s performance more effortlessly.

Here we will use the Graphviz Python library to draw diagrams.

import graphviz
graph = graphviz.Digraph("Apple's Income Statement Diagram", comment='Diagram for Analysis')  
graph.node('ns', 'Net SALES:US$83,360',fontsize="20pt")
graph.node('p', 'Product:US$65,083',fontsize="20pt")
graph.node('ip', 'Iphone:US$38,868',fontsize="5pt")
graph.node('mac', 'Mac:US$9,178',fontsize="5pt")
graph.node('ipad', 'iPad:US$8,252',fontsize="5pt")
graph.node('wr', 'Wearables, Home and Accessories:US$8,785',fontsize="5pt")
graph.node('s', 'Services:US$18,277',fontsize="20pt")
graph.node('pc', 'Cost of Sales:US$42,790',fontsize="10pt")
graph.node('sc', 'Cost of Sales:US$5,396',fontsize="10pt")
graph.node('gm', 'Gross Margin:US$35,174',fontsize="20pt")
graph.node('rd', 'Research and development:US$5,772',fontsize="10pt")
graph.node('sga', 'Selling, general and administrative:US$5,616',fontsize="10pt")
graph.node('op', 'Operating income:US$23,786',fontsize="10pt")
graph.node('oe', 'Other Expenses:US$538',fontsize="10pt")
graph.node('i', 'Income before provision for income taxes:US$23,248',fontsize="10pt")
graph.node('it', 'Provision for income taxes:US$2,697',fontsize="10pt")
graph.node('np', 'Net income:US$20,551',fontsize="10pt")
graph.edge('ns','p')
graph.edge('p','ip')
graph.edge('p','mac')
graph.edge('p','ipad')
graph.edge('p','wr')
graph.edge('ns','s')
graph.edge('p','pc')
graph.edge('s','sc')
graph.edge('pc','gm')
graph.edge('sc','gm')
graph.edge('gm','rd')
graph.edge('gm','sga')
graph.edge('rd','op')
graph.edge('sga','op')
graph.edge('op','oe')
graph.edge('oe','i')
graph.edge('i','it')
graph.edge('it','np')
print(graph.source)
graph.render(directory='doctest-output', view=True)

Output:

Image 9. 

Summary

Human brains extrapolate pictures more quickly than words and numbers.

Number and words have information buried. We can’t discover the patterns interpret the data, and draw conclusions by just reading the numbers and words.

With visual representation tools such as diagrams, graphs, etc., we can understand the data quickly and clearly. You have learned in this article how to draw a diagram with powerful python libraries.

These libraries are Graphviz, ImageDraw, and turtle.

For drawing complex diagrams without any images, Graphviz is a better choice. If you need to edit images and use them in a diagram, the ImageDraw library is better. As we know, The Software Scientist created the turtle for kids to learn python programming language.

Beginners can use Turtle for educational purposes and also for creating animated stories. Story with animated diagrams and words are more potent for the reader to understand well.

Hope this article helped you and answered your query. Give your feedback at thoufeeq87.mtr@gmail.com.

Have a great coding!