A Quick Introduction to Data Analysis With Pandas

Python is a great language for doing data analysis, primarily because of the fantastic ecosystem of data-centric Python packages. Pandas is one of those packages and makes importing and analyzing data much easier.

Pandas aims to integrate the functionality of NumPy and matplotlib to give you a convenient tool for data analytics and visualization. Besides the integration,  it also makes the usage far more better.

In this blog, I’ll give you a list of useful pandas snippets that can be reused over and over again. These will definitely save you some time that you may otherwise need to skim through the comprehensive Pandas docs.

The data structures in Pandas are capable of holding elements of any type: Series, DataFrame.

Series

A one-dimensional object that can hold any data type such as integers, floats, and strings

A Series object can be created of different values. Series can be remembered similar to a Python list.

In the below example, NaN is NumPy’s nan symbol which tells us that the element is not a number but it can be used as one numerical type pointing out to be not a number. The type of series is an object because the series has mixed contents of strings and numbers.

Now if we use only numerical values, we get the basic NumPy dtype - float for our series.

DataFrame

A two-dimensional labeled data structure where columns can be of different types.

Each column in a Pandas DataFrame represents a Series object in memory.

In order to convert a certain Python object (dictionary, lists, etc) to a DataFrame, it is extremely easy. From the python dictionaries, the keys map to Column names while values correspond to a list of column values.

Pandas and Optimizations 1.png

Reading CSV files

Pandas can work with various file types while reading any file you need to remember.

pd.read_filetype()

Now you will have to only replace “filetype” with the actual type of the file, like csv or excel. You will have to give the path of the file inside the parenthesis as the first argument. You can also pass in different arguments that relate to opening the file. (Reading a csv file? See this)

Pandas and Optimizations 3.png

Accessing Columns and Rows

DataFrame comprises of three sub-components, the indexcolumns, and the data (also known as values).

The index represents a sequence of values. In the DataFrame, it always on the left side. Values in an index are in bold font. Each individual value of the index is called a label. Index is like positions while the labels are values at that particular index. Sometimes the index is also referred to as row labels. In all the examples below, the labels and indexes are the same and are just integers beginning from 0 up to n-1, where n is the number of rows in the table.

Selecting rows is done using loc and iloc

  • loc gets rows (or columns) with particular labels from the index. Raises KeyError when the items are not found.

  • iloc gets rows (or columns) at particular positions/index (so it only takes integers). Raises IndexError if a requested indexer is out-of-bounds.

Pandas and Optimizations 3.png

Accessing the data using column names

Pandas takes an extra step and allows us to access data through labels in DataFrames.

YCombinator companies data set

In Pandas, selecting data is very easy and similar to accessing an element from a dictionary or a list.

You can select a column (df[col_name]) and it will return column with label col_name as a Series, because rows and columns are stored as Series in a DataFrame, If you need to access more columns (df[[col_name_1, col_name_2]]) and it returns columns as a new DataFrame.

Filtering DataFrames with Conditional Logic

Let’s say we want all the companies with the vertical as B2B, the logic would be:

If we want the companies for the year 2009, we would use:

Need to combine them both? Here’s how you would do it:

Get all companies with vertical as B2B for the year 2009

Get all companies with vertical as B2B for the year 2009

Sort and Groupby

Sorting

Sort values by a certain column in ascending order by using:

Furthermore, it’s also possible to sort values by multiple columns with different orders. colname_1 is being sorted in ascending order and colname_2 in descending order by using:

Grouping

This operation involves 3 steps; splitting of the data, applying a function on each of the group, and finally combining the results into a data structure. This can be used to group large amounts of data and compute operations on these groups.

df.groupby(colname) returns a groupby object for values from one column while df.groupby([col1,col2]) returns a groupby object for values from multiple columns.

Data Cleansing

Data cleaning is a very important step in data analysis.

Checking missing values in the data

Check null values in the DataFrame by using:

This returns a boolean array (an array of true for missing values and false for non-missing values).

Check non null values in the DataFrame using pd.notnull(). It returns a boolean array, exactly converse of df.notnull()

Removing Empty Values

Dropping empty values can be done easily by using:

This drops the rows having empty values or df.dropna(axis=1) to drop the columns.

Also, if you wish to fill the missing values with other values, use df.fillna(x). This fills all the missing values with the value x (here you can put any value that you want) or s.fillna(s.mean()) which replaces null values with the mean (mean can be replaced with any function from the arithmetic section).

Operations on Complete Rows, Columns, or Even All Data

Pandas and Optimizations 3.png

The .map() operation applies a function to each element of a column.

.apply() applies a function to columns. Use .apply(axis=1) to do it on the rows.

Iterating over rows

Very similar to iterating any of the python primitive types such as list, tuples, dictionaries.

The .iterrows() loops 2 variables together i.e, the index of the row and the row itself, variable is the index and variable row is the row in the code above.

Tips & Tricks

Using ufuncs (also known as Universal Functions). Python has the .apply() which applies a function to columns/rows. Similarly, Ufuncs can be used while preprocessing. What is the difference between ufuncs and .apply()?

Ufuncs is a numpy library, implemented in C which is highly efficient (ufuncs are around 10 times faster).

A list of common Ufuncs:

isinf: Element-wise checks for positive or negative infinity.

isnan: Element-wise checks for NaN and returns result as a boolean array.

isnat: Element-wise checks for NaT (not time) and returns result as a boolean array.

trunc: Return the truncated value of the input, element-wise.

.dt commands: Element-wise processing for date objects.

High-Performance Pandas

Pandas performs various vectorized/broadcasted operations and grouping-type operations. These operations are efficient and effective. They often rely on the creation of temporary intermediate objects, which can cause undue overhead in computational time and memory use.

As of version 0.13, Pandas included tools that allow us to directly access C-speed operations without costly allocation of intermediate arrays. There are two functions, eval() and query().

DataFrame.eval() for efficient operations:

To compute the sum of df1, df2, df3, and df4 DataFrames using the typical Pandas approach, we can just write the sum:

A better and optimized approach for the same operation can be computed via pd.eval():

%timeit — Measure execution time of small code snippets.

The eval() expression is about 50% faster (it also consumes mush less memory).

And it performs the same result:

np.allclose() is a numpy function which returns True if two arrays are element-wise equal within a tolerance.

Column-Wise & Assignment Operations Using df.eval()

Normal expression to split the first character of a column and assigning it to the same column can be done by using:

By using df.eval(), same expression can be performed much faster:

DataFrame.query() for efficient operations:

Similar to performing filtering operations with conditional logic, to filter rows with vertical as B2B and year as 2009, we do it by using:

With .query() the same filtering can be performed about 50% faster.

When to use eval() and query()

Two aspects: computation time and memory usage. 

Memory usage: Every operation which involves NumPy/Pandas DataFrames results into implicit creation of temporary variables. In such cases, if the memory usage of these temporary variables is greater, using eval() and query() is an appropriate choice to reduce the memory usage.

Computation time: Traditional method of performing NumPy/Pandas operations is faster for smaller arrays! The real benefit of eval()/query() is achieved mainly because of the saved memory, and also because of the cleaner syntax they offer.

Conclusion

Pandas is a really powerful and fun library for data manipulation/analysis. It comes with easy syntax and fast operations. The blog highlights the most used pandas implementation and optimizations. Best way to master your skills over pandas is to use real datasets, beginning with Kaggle kernels to learning how to use pandas for data analysis. Check out more on real time text classification using Kafka and Scikit-learn and explanatory vs. predictive models in machine learning here.


About the Author

profile.jpg

Ronak is a Software Engineer who is passionate about data science and analytics. He currently works on building applications using Python/Django. In his free time, he loves reading and trekking.