Quantcast
Channel: Like Geeks
Viewing all articles
Browse latest Browse all 104

Slicing In Python (Comprehensive Tutotial)

$
0
0

Programming in Python involves frequently working with sequential or ‘indexed’ data. Indexed data are the ones stored in such structures that allow their access using indices.
Such data can be of varying lengths, from a few elements to hundreds of thousands of elements.
However, most of the time, we are interested in working only on a small subset of the data. While the data may be of length 1000, we may be interested in processing only the first 10 elements. So we should extract only the relevant portion of the data.
Such extraction of a subset of data is called slicing. In this tutorial, we will look at the different ways of slicing various types of data in Python. We will see how slicing is used to access and modify different portions of sequential data.

 

 

Slicing Lists in Python

The most common way of representing an array in data is using Python lists. Let us first understand list indexing before diving into slicing.

List Indexing

Since lists are sequential data structures, each element in a Python list can be accessed using an integer index. This index represents the position of that element in the list.
The indexing begins at 0 and goes up to one less than the length of the list. Accessing any index out of this range will result in an error.
Let us look at a few examples of indexing.

a = [5, 12, 0, 1, 33, 7]

print(f"a = {a}")

print(f"a[0] = {a[0]}")

print(f"a[2] = {a[2]}")

print(f"a[5] = {a[5]}")

Output:

basic list indexing in python

The examples here are intuitive.
a[0] refers to the first element in a. a[5] refers to the 6th element in a.

It is also possible to use negative indexing in Python lists. It helps us access a list in reverse order.
The index -1 corresponds to the last element in the list, -2 refers to the second to last element, and so on.
Let us look at examples of negative indices as well.

a = [5, 12, 0, 1, 33, 7]

print(f"a = {a}")

print(f"a[-1] = {a[-1]}")

print(f"a[-2] = {a[-2]}")

print(f"a[-6] = {a[-6]}")

Output:

using negative indices on lists in python

Since there are 6 elements in the list, index -6 corresponds to the first element of the list.

List Slicing

In the previous section, we saw how we can access a single element in a list using integer indices.
Slicing is just an extension of indexing, in the sense that it is used to access multiple values at once, instead of a single value.
There are two ways of slicing lists in Python. In this section (and in most of the tutorial), we will look at the more commonly used and a neater version of the two, that is, using the ‘:’ operator.

The syntax for slicing lists in Python is
list_object[start:end:step]

It fetches the elements in the list beginning from index start, up to (but not including) the element at index end.
The step value indicates the increments between two successive indices. By default, the step value is 1.
For eg., if you do a[5:10:2], you will get elements of a at positions 5, 7, and 9.

The sliced result will also be a list.
Let’s work out some examples.

b = [x**2 for x in range(10)]

print(f"b = {b}")

print(f"b[3:5] = {b[3:5]}")

print(f"b[1:2] = {b[1:2]}")

print(f"b[7:12] = {b[7:12]}")

print(f"b[0:5] = {b[0:5]}")

print(f"b[0:9:2] = {b[0:5:2]}")

print(f"b[0:10:5] = {b[0:10:5]}")

Output:

basic list slicing in python

Note that in the third example, my ‘end’ index exceeds the length of the list. In such a case, Python doesn’t throw any errors. The result contains the elements up to the last index.

The ‘start’, ‘stop’, and ‘step’ are all optional values. We can skip any or all of them.
If we skip the ‘start’ index, we will get the result from the beginning of the list.
If we skip the ‘stop’, index, we will get the result till the end of the list.
If we skip both indices, we will get the entire list as the result.
We can also use negative indices to slice the list.

b = [x**2 for x in range(10)]

print(f"b = {b}")

print(f"first 5 elements = b[:5] = {b[:5]}")

print(f"b[7:] = {b[7:]}")

print(f"b[-4:-2] = {b[-4:-2]}")

print(f"last 5 elements = b[-5:] = {b[-5:]}")

print(f"all the elements of b = b[:] = {b[:]}")

print(f"elements at even positions = b[::2] = {b[::2]}")

Output:

list slicing with negative and without start,stop indices in python

NOTE that whatever rules we have discussed for list slicing also apply almost equivalently to the slicing of other data structures such as strings, tuples, etc.

 

Modifying Lists using Slicing

We saw how we can access portions of lists using slicing. We can also modify parts of a list using slicing.
The syntax for such an operation would be: l1[start:end] = l2
This will replace the portion of the list l1 represented by the slice [start:end] with the content of list l2.

Ideally, the length of the list l2 should be the same as the length of the slice.
However, if you assign a list of length greater than the sliced part, it will replace the sliced portion with the content of the entire assigned list without affecting any neighboring elements.
This will effectively increase the length of the list.
On the other hand, if the length of the assigned list is less than the length of the sliced part, it will remove the entire sliced portion from the list and replace it with the content of the shorter assigned list.
This will effectively reduce the size of the list.

Let’s understand these with the help of examples.

c = list(range(11,25))

print(f"c = {c}\n")

# replacing first 3 elements by 100
c[:3] = [100, 100, 100]

print(f"After replacing first 3 elements by 100 in original list:\nc = {c}\n")

# replacing four elements by 100
c = list(range(11,25))

c[-7:-3] = [100, 100, 100, 100]

print(f"After replacing four elements at c[-7:-3] by 100 in original list:\nc = {c}\n")

# Assigining a longer list to smaller slice
c = list(range(11,25))

d = [100, 100, 100]

print(f"d = {d}, length of d = {len(d)}")

print(f"c[2:4] => {c[2:4]}")

c[2:4] = d

print(f"After, assigning c[2:4] = d,\nc: {c}\n")

# Assigining a shorter list to a larger slice
c = list(range(11,25))

d = [100, 100]

print(f"d = {d}, length of d = {len(d)}")

print(f"c[-4:] => {c[-4:]}")

c[-4:] = d

print(f"After, assigning c[-4:] = d,\nc: {c}")

print(f"Now c[-4:] => {c[-4:]}\n")

Output:

modifying list using slicing

In the second-last example, we are assigning a list of length 3 to a slice of length 2. This will increase the length of the list by one.
In the last example, we assign a list of length 2 to a slice of length 4. The entire slice portion in the original list is replaced by this 2-element list, thus shortening the total length of the list by 2.

 

Flip a List in Python with Slicing

Reversing a list can be tricky.
If you try doing it using a straightforward approach, you might need to write a function that creates a new list, iterates over the list to be reversed, and appends the items to the new list in reverse order.
You won’t need to do all of this if you understand how slicing works. With a single slice operation, you can flip any list.
All you have to do is, begin slicing from the end of the list to the start of the list with a step value of -1.
So it will be l[n-1::-1], where n is the length of the list.

Since we are fetching the entire list (in reversed order), we can skip both the start and end positions, and specify the step value.
In this case, the flipped list can be created using l[::-1]

a = [1, 2, 3, 4, 5]
a_reversed = a[::-1]

print(f"a = {a}")
print(f"a flipped = {a_reversed}\n")

b = ["John", "Lenin", "Walter", "Fabian"]
b_reversed = b[::-1]

print(f"b = {b}")
print(f"b flipped = {b_reversed}")

Output:

flipping lists using slicing

Note that, unlike many list operations, slicing is immutable.
It creates and returns a new list containing the sliced content. In the above code example, lists a and b would remain unchanged.

 

String Slicing in Python

As discussed earlier, the slicing operation is not limited to just lists. It can be extended to any data structure whose individual elements can be accessed using indices.
In this section, we will use slicing to perform various operations on strings.

Finding a substring using slicing

We will use the same slicing techniques, as we did in lists to fetch different substrings from a string.

s = "Cinderella has long hands and a beautiful nose"

s1 = s[:10]

s2 = s[-4:]

s3 = s[15:25]

print(f"s = {s}")

print(f"s[:10] = {s1}")

print(f"s[-4:] = {s2}")

print(f"s[15:26] = {s3}")

Output:

basic use of string slicing in python

We can also combine string search with string slicing. We use the find method to find the index of a particular substring and use it as start_index to perform slicing from there.

start_ind = s.find("bea") #finding start index

s4 = s[start_ind: start_ind+10] #slicing string of length 10 from start index

print(f"s[start_ind: start_ind+10] = {s4}")

Output:

combining string search with slicing

Deleting a character from string using slicing

If we want to delete a character from a string, we first find its index using the find() method. Then, using string slicing, we fetch the substring before and after that character.
By concatenating the before and after strings, we effectively remove the desired character from the string.

s = "I love eating toasted cheese and tuna sandwiches."

l_index = s.find("l")

modified_s = s[:l_index] + s[l_index+1:]

print(f"Original string: {s}")

print(f"After removing letter l: {modified_s}")

Output:

removing a character using string slicing

Note that here the letter ‘l’ occurs only once in the string and we could remove that using string slicing.
However, if we use the same code to remove a repetitive character like ‘t’, it would delete only the first occurrence of the character in the string.
However, we can use the same approach iteratively to remove all the occurrences of the character.

s = "I love eating toasted cheese and tuna sandwiches"

print(f"Original  string:{s}\n")

new_s = ""

start_index = 0

t_index = s.find('t')

while(t_index != -1):

    new_s = new_s + s[start_index:t_index]

    start_index = t_index+1

    t_index = s.find('t', start_index)
    
new_s += s[start_index:]

print("After removing all 't's:",new_s)

Output:

removing all occurrences of a character using string slicing

Thus, we could remove all occurrences of ‘t’ from the string using string slicing iteratively on different substrings.

Replacing a word in a Python string using slicing

We can use the same technique that we used to delete a character to replace a word in a string with another word.
We first find the position of the word in the string using find,
then we slice the substrings before and after the word and insert our new word in between by concatenating all three.

# replacing a word in string using slicing
s = "Pineapple pizza is better than chicken burger"

word = "pizza"

new_word = "custard"

start_ind = s.find(word)

new_s = s[:start_ind]+ new_word+ s[start_ind+len(word):] #inserting new word

print(f"Original string = {s}\n")

print(f"replacing '{word}' by '{new_word}' in the string\n")

print(f"Updated string = {new_s}")

Output:

replacing a word in a string using slicing

Note that this method will replace only the first occurrence of a word in the string.

Reversing a Python string using slicing

We can use the same slicing that we used to flip a list to reverse a string.
We will omit the start and end index and specify the step size as -1.

s = "The sun rises in the east"

print(f"string s = {s}\n")

rev_s = s[::-1]

print(f"s reversed = {rev_s}")

Output:

reversing a string using slicing

Note that the string here has been entirely reversed on the character level, making it look completely gibberish.
We can also do the word-level reverse by first splitting the words into a list, flipping the list, and then joining the words back into a string.

s = "The sun rises in the east"

print(f"string s = {s}\n")

words = s.split(" ") #splitting the string into words

words_rev = words[::-1] #reversing the positions of words

rev_s = " ".join(words_rev) #joining the words into string

print(f"s reversed = {rev_s}")

Output:

word-level string reversing using slicing

Now the individual words are intuitive and non-gibberish.

 

Slicing using slice() method

In addition to the more popular way of slicing that we have discussed so far (using the ‘:’ operator), there also exists another way.
The slice() method accepts 3 arguments – startstep, and stop, and generates a slice object that we can use to slice any iterable.

a = list(range(50))

s0 = slice(10,15)

print(f"s0: {s0}\n")

print(f"type(s0): {type(s0)}\n")

print(f"a[s0] = {a[s0]}")

Output:

slice object introduction

If we pass only 1 value to the method, it is treated as the stop value, and the iterable will be sliced from the beginning to the position specified by that value.
Also, the way we could omit the ‘start’ or ‘stop’ or ‘step’ or all of them in the previous approach, we can do the same with the slice method by passing the value None for the respective parameters.
Additionally, negative indices also work with the slice method as well as they did with the previous approach.

Let us look at a few examples using a slice object generated by the slice() method.

l = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]

print(f"list l = {l}\n")

print("Slices using slice() method:")

s1 = slice(2,5)

print(f"l[2:5] = {l[s1]}")

s2 = slice(6)

print(f"l[:6] = {l[s2]}")

s3 = slice(-2,None)

print(f"l[-2:] = {l[s3]}")

s4 = slice(None,8, 2)

print(f"l[:8:2] = {l[s4]}")

Output:

list slicing using the slice method

We can similarly use the slice object to slice all valid data structures, including strings.
Let us look at a few examples of string slicing using the slice() method.

name = "Suvendhu Adhikari"

print(f"name = {name}\n")

print("Slicing name string using slice() method:")

s1 = slice(5)

print(f"name[:5] = {name[s1]}")

s2 = slice(3,10)

print(f"name[2:10] = {name[s2]}")

s3 = slice(-8, None)

print(f"name[-8:] = {name[s3]}")

s4 = slice(None, None, -1)

name_rev = name[s4]

print(f"string reversed using slice: {name_rev}")

Output:

string slicing using the slice method

 

Slicing NumPy arrays

NumPy arrays are one of the most commonly used data structures used for processing multi-dimensional data in Python.
Slicing of 1-dimensional NumPy arrays is fairly intuitive and works in the same way as Python list slicing does.
Here are a few examples.

import numpy as np

arr = np.array([10, 20, 30,  40, 50, 60, 70])

print(f"arr = {arr}\n")

print(f"arr[:3] = {arr[:3]}")

print(f"arr[-2:] = {arr[-2:]}")

print(f"arr[2:6] = {arr[2:6]}")

print(f"arr[1:7:2] = {arr[1:7:2]}")

print(f"arr reversed = {arr[::-1]}")

Output:

basic use of numpy slicing

Multidimensional NumPy arrays are where the power of slicing is realized.
We can specify as many slices as there are dimensions of the NumPy array.
First, let us look at the slicing on the first dimension of a 2D array. This slicing will apply to the rows of the 2D array.

    arr = np.array([[1, 2, 3, 4, 5],
    [11, 12, 13, 14, 15],
    [21, 22, 23, 24, 25],
    [31, 32, 33, 34, 35]])

print(f"arr:\n{arr}\n")


print(f"arr[:2]:\n{arr[:2]}\n")

print(f"arr[-2:]:\n{arr[-2:]}\n")

print(f"arr[1:4]:\n{arr[1:4]}\n")

print(f"reversing rows of arr:\n{arr[::-1]}")

Output:

1d slicing on 2d numpy arrays

In all the examples here, we have performed slicing on the first dimension i.e the rows of the 2D array. In all the sliced results, all 5 columns of the array were retained.

We can also slice along the second, third, and higher dimensions of multidimensional arrays by specifying the respective slicing indices separated by a comma.
Now, let us also slice along the columns of the 2D array.

arr = np.array([[1, 2, 3, 4, 5],
                [11, 12, 13, 14, 15],
                [21, 22, 23, 24, 25],
                [31, 32, 33, 34, 35]])

print(f"arr:\n{arr}\n")


print(f"arr[:, :2]:\n{arr[:, :2]}\n") #all rows, 1st 2 columns

print(f"arr[:3, -2:]:\n{arr[:3, -2:]}\n") #1st 3 rows, last 2 columns

print(f"arr[1:3, 3:5]:\n{arr[1:3, 3:5]}\n") #2nd and 3rd rows, 4th and 5th columns

print(f"arr[2, 2:4]:\n{arr[2, 2:4]}\n") #3rd row, 3rd and 4th columns


arr_col_rev = arr[:,::-1]

print(f"all columns flipped:\n{arr_col_rev}\n")

row_col_rev = arr[::-1, ::-1]

print(f"rows and columns both reversed:\n{row_col_rev}\n")

Output:

slicing on 2d numpy arrays

Thus, we can slice on one or all the dimensions of a multidimensional NumPy array.
We could also reverse the rows and the columns of the array. When we do both together, we effectively flip the whole matrix along its diagonal.

 

Slicing Pandas Dataframes

Pandas Dataframe is one of the most popular data structures used for representing tabular data.
A table, as you know, consists of multiple rows and columns. Pandas DataFrames enable you to access these rows and columns using integer indexing.
This opens up a lot of scope to slice and dice the data stored in these dataframes along one or both dimensions.

Let us first define a simple Pandas dataframe containing basic information of a few people such as name, age, gender, etc. We will then perform slicing on this data.

import pandas as pd

df = pd.DataFrame([["Dalton", 32, "M", 72, 155],
                   ["Jack", 25, "M", 80, 175],
                   ["Emily", 30, "F", 54, 140],
                   ["Daniel", 45, "M", 85, 167],
                   ["Mariyam", 27, "F", 65, 152],],
                 columns=["Name", "Age", "Gender", "Weight(kg)", "Height(cm)"])

print(df)

Output:

defining a pandas dataframe

The dataframe consists of five rows and five columns. We can access the individual rows/columns using the method .iloc.
We have to specify two indices to iloc, separated by a comma. The first of these indices refers to the positions of rows in the tables, and the second one refers to the positions of columns.

Let us look at examples of both indexing individual rows and columns, and performing slicing on the data.

r = df.iloc[2]

print(f"row at index 2:\n{r}\n")

c = df.iloc[:,3]

print(f"column at index 3:\n{c}\n")

d1 = df.iloc[:3,:]

print(f"first 3 rows:\n{d1}\n")

d2 = df.iloc[:,-2:]

print(f"last 2 columns:\n{d2}\n")

d3 = df.iloc[2:4,1:3]

print(f"row 2 to 3, column 1 to 2:\n{d3}\n")

Output:

slicing a pandas dataframe

As you can see, the slicing operations are similar to the ones we saw earlier with NumPy’s 2D arrays.
Let us also try reversing the order of rows and columns.

df_row_rev = df.iloc[::-1, :]

print(f"DF with row order reversed:\n{df_row_rev}\n")

df_col_rev = df.iloc[:, ::-1]

print(f"DF with row order reversed:\n{df_col_rev}\n")

df_both_rev = df.iloc[::-1, ::-1]

print(f"DF with row and column order reversed:\n{df_both_rev}\n")

reversing dataframe rows and columns using slicing

 

Conclusion

In this tutorial, we understood the importance of extracting portions of sequential data and looked at various ways of achieving this using slicing in Python.
We learned the syntax and usage of slice operations on lists. We sliced different portions of the list.
Then we saw how we can modify an existing list using the Python slice operation. We learned a technique to flip lists using the slicing operation in Python.

Next, we used slicing on strings. After looking at the basic usage of slicing to find different substrings, we saw various applications of slicing on strings, such as deleting a character from a string, replacing a word in a string with another word, and reversing a string.
We took a brief detour to understand another way of slicing objects using the in-built slice method. We used the slice object returned by this method to slice lists and strings in the same way as we could using the ‘:’ operators.

Also, we sliced 1-D and 2-D NumPy arrays. We also reversed the order of rows and columns in a 2D array using slicing. Finally, we used slicing to slice rows and columns of a Pandas DataFrame. We also used it to reverse the order of the rows and columns in the table.

The post Slicing In Python (Comprehensive Tutotial) appeared first on Like Geeks.


Viewing all articles
Browse latest Browse all 104

Trending Articles