PYTHON DATA STRUCTURES..
Tags: python, machinelearning, datascience, programming
Published: 2021-10-14 15:32:59

The most basic data type in python is the sequence. This involves storing data in a particular structure which is characterized by an index and values; where the index reference the position of the element/value in the structure.
The indices referencing start from 0 the structures permit some common operations such as indexing, slicing, adding, multiplying among others.
There are around six built in types of sequences but the most common are lists, dictionaries, tuples and sets.

#### Python Lists. 
Lists are ordered sequence of comma – separated items within a square bracket. The square bracket indicates the beginning and the end of the specified list.

##### Creating a List.
A list is created when values enclosed in a square brackets are assigned to a variable as shown below:
List_1 = [ ]      - This creates an empty list.
List_2 = [ 1, 2, 3, 4, 5, 6 ]        - A list containing numbers.
List_3 = [ “a”, “b”, “c”, “d”, “e”, “f”  ]       -A list containing alphabets
The items contained within the list can be of different data types e.g. numbers, strings or even a list itself.
List_4 = [ “Monday”, “Wednesday”, 890, 325, [1998, 1999, 2002], “Sunday” ]

##### Accessing List Values.
To access an element(s) at a given position in a list, use the square bracket for slicing with the specific index to obtain the target value. Indexing out of the bounds of possible list values will result into a n IndexError.
```py
List_2 = [ 1, 2, 3, 4, 5, 6 ]
List_3 = [ “a”, “b”, “c”, “d”, “e”, “f”  ]       
print ("List _1[0]: ", List _1[0]) 
print ("List _2[1:5]: ", List _2[1:5])
```
When the above code is executed, it results to :-
```
List_1[0] :  1
List_2[1:5] :  [“b”, “c”, “d”, “e”, “f”  ]
```

##### Update Lists.
To update a single or multiple values of a list, assign the slice on the left hand side of the assignment to the list to update at a specific index.

```py
#Empty List
List_1 = [ ] 

#Add 3 to the beginning of the empty list .
List_1 [0]  = 3
print(List_1)

List_2 = [ 1, 2, 3, 4, 5, 6 ]
print ("Value at index 3 :" , List_2 [ 3])
List_2 [ 3] = "Mathematics"
print ("New value at index 3 :" , List_2 [ 3])
```
When the above code is executed, it results to :-
```
Value at at index 3 : 4
 New value at index 3 : Mathematics
```

##### Delete List Elements.
To delete/remove a list element, use either **del** statement if you know the actual element to delete or **remove()** method if you aren't exactly aware of the element to remove.
```py 
List_5 = [1, 3.14, “abc”, 5, 7, 8]
print(List before deleting value at index 2 : ", List_5)

del List_5[2]
print("List after deleting value at index 2 : ", List_5)
```
The above code will result into:-
```
List before deleting value at index 2 :  [1, 3.14, "abc", 5, 7, 8]

List after deleting value at index 2 :  [1, 3.14, 5, 7, 8]
```

##### Basic List Operations .
The list structure accepts basic multiplication, addition, subtraction and the resultant structure is a list as well.
Lists can be added and multiplied in the same way as strings. The output is a result of concatenation and repetition just like in strings.
```py
List_2 = [ 1, 2, 3, 4, 5, 6 ]
Print(List_2 + [7,8,9])

Num = [9,8,7]
Print(num * 3)
```
Which produces the following output:
```
[1, 2, 3, 4, 5, 6, 7, 8, 9]

[9,8,7,9,8,7,9,8,7]
```
To check whether a given element exists in a list, the `in` operator is used. It returns True if the item exists and False if it doesn’t.
```py
Num = [9,8,7]
print(8 in Num)
print(3 in Num)
```
When the above code is executed, it results in:
```
True
False
```
We can also use the `not` operator to check if an item is not in a list.
Evaluate the code below to check the output.
```py
Num = [9, 8, 7]
Print( not 4 in Num )
Print( 4 not in Num )
Print( not 8 in Num )
Print( 8 not in Num )
```

##### List Slicing.
List slicing is the truncation of the list at a given range specified by an index.
Lets use the following list to perform various slicing operations.

```py
List_5 = [1, 3.14, "abc", 5, 7, 8]

# To print the entire list items
print(List_5[:])

# To print from a given index (say 2 )  to  the end.
print(List_5[2:])

# To print from a given index (say 2 )  to  another index (say 4).
print(List_5[2:4])

# To print from the beginning  to  the second last item.
print(List_5[ : -1])

# To print from the beginning  to  the third last item.
print(List_5[ : -1])

# To print from a given index (say 1)   to  the second last item.
print(List_5[ 1 : -1])

# To reverse the list
print(List_5[ : : -1])


```
The various outputs will be as outlined below
```
[1, 3.14, 'abc', 5, 7, 8]
['abc', 5, 7, 8]
['abc', 5]
[1, 3.14, 'abc', 5, 7]
[1, 3.14, 'abc', 5]
[3.14, 'abc', 5, 7]
[8, 7, 5, 'abc', 3.14, 1]
```

#### Python Dictionaries.
Dictionaries are key – value items enclosed within a curly braces. Each key is separated from its value by a colon (:). A pair of key and value forms a dictionary item. The items are separated by commas and enclosed in curly braces. The curly braces indicates the beginning and the end of the specified dictionary.
Dictionary keys are very unique and are used to  reference the given values which may not be unique and can be of any data type. They are of immutable data types such as strings, numbers or tuples. Key duplication isn’t supported. 
The dictionary values have no restrictions and can be any python object.

Dictionaries are generally unordered i.e. the order of items in a dictionary will not necessarily be the order in which we put them during creation. This is because python internally rearranges the elements in order to optimize performance.

Creating Dictionaries:
```py
# Empty Dictionary
dict = { }

# Dictionary with data
dict = { ‘food’ : ‘Pilau’ , ‘drink’: ‘Soda’, ‘fruit’: ‘strawberry’}
```
Each entry consist of a pair of items separated by a colon. The first part is called the key i.e food, drink, fruit and the second part is the value i.e Pilau, Soda, Strawberry.

###### Accessing Values of a Dictionary.
To access a given dictionary value, use Square brackets along with the key to obtain its value.
```py
dict = { ‘food’ : ‘Pilau’ , ‘drink’: ‘Soda’, ‘fruit’: ‘strawberry’}

print ("dict[‘food’]: " ,  dict[‘food’])
 print ("dict[‘fruit’]: " ,  dict[‘fruit’])
```
When the above code is executed, it produces the following result –
```
dict[‘food’]:  Pilau
 dict[‘fruit’]: strawberry
```
Accessing dictionary data using a key that is not part of the dictionary generates an error.

###### Modifying a  Dictionary Value.
A dictionary can be modified by adding a new entry (key – value pair), changing an existing entry or deleting an existing entry as shown.
```py
dict = { ‘food’ : ‘Pilau’ , ‘drink’: ‘Soda’, ‘fruit’: ‘strawberry’}

# To add new entry to the dictionary
dict['age'] = 28

#  To change `drink` from soda to water use:
dict['drink'] = water
```
When deleting an entry, you can either remove individual dictionary elements or clear the entire dictionary contents.
To delete a specific entry, use the `del` statement and specify the index of the element. When `del` statement is used on a dictionary without defining the key attribute then the entire dictionary is deleted. However, to only clear the dictionary contents and preserve the dictionary itself us the `clear` function. This clears every element and returns an empty dictionary.
```py
dict = { ‘food’ : ‘Pilau’ , ‘drink’: ‘Soda’, ‘fruit’: ‘strawberry’}

# Remove entry with key ‘fruit
del dict[‘fruit’]

#  Remove all entries  in the dictionary
del dict

# Remove all entries of the dictionary.
dict.clear()
```
###### Copying Dictionaries.
Use the `copy()` method to copy a dictionary.
```py
dict = { ‘food’ : ‘Pilau’ , ‘drink’: ‘Soda’, ‘fruit’: ‘strawberry’}

d = dict.copy()
```

###### The in operator.
The in operator checks if a given item is a key in the dictionary. 
Referring to a key that is not in the dictionary produces an error and using the in operator to first check for the availability of the key prevents the error.
```py
dict = { ‘food’ : ‘Pilau’ , ‘drink’: ‘Soda’, ‘fruit’: ‘strawberry’}

# Check if a given letter/word is a key in dict
letter = input( ‘Enter a letter / word’)
if letter in dict
	print(“The value is: ’ , dict[letter]
else:
	print(“Not in dictionary”)
```
Looping in dictionaries is similar to that of lists

```py
for key in dict
	print(key)		# Keys
	print(dict[key])	# Values
```
###### Manipulating Lists of Keys and Values.
Using the dictionary d = {‘A’:1, ‘B’:2}
```py
d = {‘A’:1, ‘B’:2}

# To generate a list from the dictionary keys
list(d)

# To generate a list from the dictionary values
list(d.values())

# # To generate a list from the dictionary keys – values pair
list(d.items())
```
When executed, it produces the following output.
```
['A', 'B']
[1, 2]
[('A', 1), ('B', 2)]
```
The pair returned by `d.items()` are called tuples.

The `dict()` function can also create a dictionary. It’s almost like the opposite of the items method above.
```py
d  = dict([(‘z’,26), (‘x’,24)])
Print(d)
```
This will output the following dictionary:-
```
{'z': 26, 'x': 24}
```
###### Dictionary comprehensions.
This works similar to the list comprehensions.
For example, to create a dictionary from a list of words, where the values are the length of the words.
```py
d = {s : len(s) for s in words}
```

#### Python Tuple.
A tuple is a sequence of immutable python objects that are enclosed within parenthesis and separated by comma.
Tuple is similar to list. Only difference is that list is enclosed between square bracket, tuple between parenthesis and List have mutable objects whereas Tuple have immutable objects.
Tuples are immutable, i.e. they cannot be updated and are considered read-only lists.
##### Why Use Tuple?
1. Processing of Tuples are faster than Lists.
2. It makes the data safe as Tuples are immutable and hence cannot be changed.
3. Tuples are used for String formatting.

##### Creating Tuples.
Tuples are created by:
* use of brackets
If Parenthesis/bracket is not given with a sequence, it is by default treated as Tuple.
```py
b = (1,2,'T',7.9)
c="a",10,20.9 
print(b)
print(c)
```
Output:-
```
(1, 2, 'T', 7.9)
("a",10,20.9)
```
Tuples are immutable, therefore one can only create a new tuple from portions of an existing tuple.
But there can exist an empty Tuple also which contains no element.
```py
tuple_1 = ()
```
For a single valued tuple, there must be a comma at the end of the value.
```py
tuple_1=(103,)
```
##### Accessing Tuples
Tuples just like lists are accessed using index referencing.  
```py
tuple_1=(103,104,105,106)
tuple_2=('Marina', 'Naftal','Joanne','Alex')

print(tuple_1[0])
print(tuple_1[0:2])
print(tuple_2[-3:-1])
print(tuple_1[0:])
print(tuple_2[:2])
```
This will output:
```
103
(103, 104)
('Naftal', 'Joanne')
(103, 104, 105, 106)
('Marina', 'Naftal')
```
##### Deleting Tuple Elements
Tuples being immutable, it is impossible to remove individual elements from it. 
To explicitly remove an entire tuple, use `del` statement.
```py
tuple_2 = ('Marina', 'Naftal','Joanne','Alex')
del tuple_2
```

* Tuple Operations
Operations performed on a tuple can be given as:

##### Adding a tuple
Tuple elements can be added together using the concatenation operator (+) to join the two tuples.
```py
tuple_1=(103,104,105,106)
tuple_2=('Marina', 'Naftal','Joanne','Alex')
tuple_3=tuple_1+tuple_2
print(tuple_3)
```
This results into:-
```
(103, 104, 105, 106, 'Marina', 'Naftal', 'Joanne', 'Alex')
```
##### Replicating a Tuple
This means repeating the tuple elements and can be done using '*' operator.

```py
tuple_4=(10,20,30);
print(tuple_4*2)
``` 
This results into:-
```
(10, 20, 30, 10, 20, 30)
```

#### Python Sets.

A set is a data structure with the following properties
-	The elements are unordered.
-	The elements are unique. i.e. appear only once.
-	The elements are unindexed.

Sets can be of any data type and are used when:
-	The order of data doesn’t matter.
-	Unique elements are required.

###### Creating Sets.
Sets are created by 
* use of curly brackets, e.g.
```py 
a = {1,5,'s',1.5}
print(a)
print(b)
```
The following code produces  the output:-
```
{1, 's', 5, 1.5}

```
* Use of a constructor, e.g

```py
f = set([2,3,4])
print(f)
```
Which produces the following output:-
```
{2, 3, 4}
```

* Set Operations

##### Finding the length of a set
This is done using the len() function.
```py
f = set([2,3,4])
print(len(f))
```
This will produce:
```
4
```

##### Accessing elements of a set.
Set elements can only be accessed using loops but not indices. 
```py
a = {1,5,'s',1.5}
for x in a:
    print(x)
```

##### Adding elements to a set.
The `add()` function allows the addition of a single element to a set.
The `update()` function allows the addition of more than one element to a set.
```py
c = set()
c.add(8)
print(c)
c.update([1,5,'s',1.5])
print(c)
```
When executed it produces the following results:
```
{8}
{1, 1.5, 5, 8, 's'}
```

##### Removing elements from a set.
   a) `remove()` function removes elements from the set specified as a parameter.
   b) `discard()` function is used to remove an element when it's existence in the set is not assured.
    c) `pop()` function is used to remove a random element from the set.
```py
c = { 1, 1.5, 5, 8, 's'}
c.remove(1.5)
print(c)
```
The result is:-
```
{1, 5, 8, 's'}
```

##### Union of sets.
Se union is the concatenation of two or more sets into a single set.
To unite sets, we use the pipeline symbol (|) or we can apply the `union` method as shown below.
```py
c = { 1, 1.5, 5, 8, 's'}
f = {10, 2, 3, 4}
d = {7,'i','p',45}
print(c|f)
print(c.union(d))
print(c|f|d)

```
Execution of the above code results to:-
```
{1, 1.5, 2, 3, 5, 4, 8, 10, 's'}
{1, 1.5, 5, 7, 8, 's', 45, 'i', 'p'}
{1, 1.5, 2, 3, 5, 4, 7, 8, 10, 's', 45, 'i', 'p'}
```

##### Intersection of sets.
Intersection applied on two or more sets is the formation of a new set consisting of only common elements present in the merged sets.
This is done using the ampersand symbol (&) or by using the `intersection` method as shown below.

```py
c = { 1, 10, 5, 8, 's'}
f = {10, 2, 3, 4,5}
d = {7,'i','p','s'}

print(c&f)
print(c.intersection(d))

# Results into an empty set
print(c.intersection(d,f))  
```

Output:- 
 
```
{10, 5}
{'s'}
set()
```
##### Difference of sets.
Set difference applied on two or more sets produces a new set of elements that are present in only one of those sets. This is achieved using the '-' symbol or the `difference()` method.
```py
c = { 1, 10, 5, 8, 's'}
f = {10, 2, 3, 4,5}
d = {7,'i','p','s'}

print(c-f)
print(f-c)
print(c.difference(d))
print(c.difference(d,f))
```

When the code above is run, it generates the following output:-
```
{8, 1, 's'}
{2, 3, 4}
{8, 1, 10, 5}
{1, 8}
```

##### Frozen sets.
A frozen set is a locked set whose values cannot be modified. i.e. one cannot add or remove values from a frozen set. It's immutable.
A frozen set is created using the `frozenset()` function.
```py
a = {1,3,5,7,'y',89}
b = frozenset(a)
print(b)
```
This outputs:
```
frozenset({1, 'y', 3, 5, 7, 89})
```

An error is generated when one tries to add or remove an element from a frozen set.
```py
b.add(8)
```
Output:-
```
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'frozenset' object has no attribute 'add'

```