Data handling and manipulation with Pandas

  14 minute read   Follow @GaryLi

What is pandas?

Pandas is python data analysis library which provides integrated and function rich utilities for data handling, wrangling, and analysis of the datasets.

How to install pandas?

# using Miniconda
# to install specific version provide pandas=version number (e.g. pandas=1.0)
conda install pandas

# using pip
pip install pandas

Check pandas version

# you can use interactive python interpreter, jupyter notebook or python code
# I am using interactive python interpreter (Python 3.7)
>>> import pandas as pd
>>> pd.__version__
'0.25.3'

Create dataframe and series using pandas

# you can use interactive python interpreter, jupyter notebook or python code
# I am using interactive python interpreter (Python 3.7)

# create pandas dataframe (two-dimensional)
>>> import pandas as pd
# dataframe from dict
>>> df1 = pd.DataFrame({'col1':['A', 'B', 'C', 'D', 'E'], 'col2':[1,2,3,4,5], 'col3':[0.1,0.2,0.3,0.4,0.5]})
>>> df1
col1 	col2 	col3
0 	A 	1 	0.1
1 	B 	2 	0.2
2 	C 	3 	0.3
3 	D 	4 	0.4
4 	E 	5 	0.5

# Create a dataframe with some random numbers
>>> import pandas as pd
>>> import numpy as np
>>> df2 = pd.DataFrame(np.random.randint(0,100,size=(1000, 10)), columns=['col%i' % i for i in np.arange(10)])
# print first 5 rows
>>> df2.head()
   col0  col1  col2  col3  col4  col5  col6  col7  col8  col9
0     2    72     7    23    82    76    29    86    55    41
1    37    23    73    62    53    70    50    30     5    11
2    38    13    64    54    19    35    93    67    25    13
3    55    58    82    92    35    24     0    57     7     6
4    75    65    82    68    16    49    50    86    52    65

# create pandas series (one-dimensional)
df3 = pd.Series(np.random.randint(0,100, size=10))
>>> df3.head()
0     5
1    37
2    59
3    53
4    37
dtype: int32

Import data from file

# read csv file
>>> import pandas as pd
>>> df4 = pd.read_csv("https://reneshbedre.github.io/assets/posts/volcano/testvolcano.csv")
>>> df4.head()
          GeneNames  value1  value2    log2FC       p-value
0  LOC_Os09g01000.1    8862   32767 -1.886539  1.250000e-55
1  LOC_Os12g42876.1    1099     117  3.231611  1.050000e-55
2  LOC_Os12g42884.2     797      88  3.179004  2.590000e-54
3  LOC_Os03g16920.1     274       7  5.290677  4.690000e-54
4  LOC_Os05g47540.4     308      18  4.096862  2.190000e-54

# read tab file
>>> df5 = pd.read_csv("https://reneshbedre.github.io/assets/posts/anova/onewayanova.txt", sep="\t")
>>> df5.head()
    A   B   C   D
0  25  45  30  54
1  30  55  29  60
2  28  29  33  51
3  36  56  37  62
4  29  40  27  73

# make first column of table as index
>>> df6 = pd.read_csv("https://reneshbedre.github.io/assets/posts/volcano/testvolcano.csv", index_col=0)
>>> df6.head()
                  value1  value2    log2FC       p-value
GeneNames
LOC_Os09g01000.1    8862   32767 -1.886539  1.250000e-55
LOC_Os12g42876.1    1099     117  3.231611  1.050000e-55
LOC_Os12g42884.2     797      88  3.179004  2.590000e-54
LOC_Os03g16920.1     274       7  5.290677  4.690000e-54
LOC_Os05g47540.4     308      18  4.096862  2.190000e-54

# import specific columns only
# you can also replace numbers with column names in usecols argument
>>> df7 = pd.read_csv("https://reneshbedre.github.io/assets/posts/volcano/testvolcano.csv", usecols=[0,3,4])
>>> df7.head()
          GeneNames    log2FC       p-value
0  LOC_Os09g01000.1 -1.886539  1.250000e-55
1  LOC_Os12g42876.1  3.231611  1.050000e-55
2  LOC_Os12g42884.2  3.179004  2.590000e-54
3  LOC_Os03g16920.1  5.290677  4.690000e-54
4  LOC_Os05g47540.4  4.096862  2.190000e-54

# skip first n rows
# you can also skip rows from bottom using skipfooter argument
>>> df8 = pd.read_csv("https://reneshbedre.github.io/assets/posts/volcano/testvolcano.csv", index_col=0, skiprows=5, header=None)
>>> df8.head()
                    1     2         3             4
0
LOC_Os05g47540.4  308    18  4.096862  2.190000e-54
LOC_Os09g00999.1  339  1213 -1.839222  1.950000e-54
LOC_Os01g62900.1  193     4  5.592457  1.560000e-54
LOC_Os09g27030.2  504   162  1.637430  1.360000e-54
LOC_Os01g51410.2  225    22  3.354350  6.760000e-54

# read only first n rows
>>> df9 = pd.read_csv("https://reneshbedre.github.io/assets/posts/volcano/testvolcano.csv", index_col=0, nrows=100)
>>> df9
                  value1  value2    log2FC       p-value
GeneNames
LOC_Os09g01000.1    8862   32767 -1.886539  1.250000e-55
LOC_Os12g42876.1    1099     117  3.231611  1.050000e-55
LOC_Os12g42884.2     797      88  3.179004  2.590000e-54
LOC_Os03g16920.1     274       7  5.290677  4.690000e-54
LOC_Os05g47540.4     308      18  4.096862  2.190000e-54
[100 rows x 4 columns]

# skip comment lines
# below command will skip any lines starting with #
# only one character allowed for comments (# is okay, but ## will not work)
>>> df = pd.read_csv("file.csv", comment='#')

Check data type (dtype)

Check a data type of each column in the dataframe

>>> df1.dtypes
Col1     object
col2      int64
col3    float64
dtype: object

Check dataframe dimensions (size, shape and ndim)

Check the total number of elements, columns, and rows in a dataframe

# get total number of elements in a dataframe 
>>> df1.size
15
>>> df2.size
10000

# get number of rows and columns
>>> df1.shape
(5, 3)  # (rows, columns)
>>> df2.shape
(1000, 10) # (rows, columns)

# get the array dimensions (number of axes)
# returns 1 for pandas series (one-dimensional) and 2 for dataframes (two-dimensional) 
>>> df1.ndim
2
>>> df2.ndim
2
>>> df3.ndim
1

Extracting columns and rows from dataframe

# get column data based on column name
>>> df1['col2']
0    1
1    2
2    3
3    4
4    5
Name: col2, dtype: int64

# get row data based on row index
# return row output will be series 
# extract row with index 0 
>>> df1.loc[0] 
Col1      A
col2      1
col3    0.1
Name: 0, dtype: object

# assign first column (col1) as index
>>> df1_1 = df1.set_index('col1')
>>> df1_1
      col2  col3
col1
A        1   0.1
B        2   0.2
C        3   0.3
D        4   0.4
E        5   0.5

# get rows data based on row index (col1 as index)
>>> df1_1.loc['E']
col2    5.0
col3    0.5
Name: E, dtype: float64

Extracting subsets of dataframes

# get subset of dataframe [row, column]
>>> df1_1.loc['A':'C', 'col2':'col3']
      col2  col3
col1
A        1   0.1
B        2   0.2
C        3   0.3

# based on default numeric index
>>> df2.loc[5:9, 'col2':'col6']
   col2  col3  col4  col5  col6
5    49     5    61    87    23
6    20    57    40    21    17
7    30    30    38    94    50
8    98    17    31    67    59
9    39     9     4    17     8

# get specific columns with all rows
>>> df2[['col1', 'col5', 'col8']]
     col1  col5  col8
0      21     2    14
1      93    29    93
2      23    24    32
3      26    72    55
4      17    96    40

# get specific rows with all columns
>>> df2[2:6]
   col0  col1  col2  col3  col4  col5  col6  col7  col8  col9
2     4    23    64    49    71    24    97    92    32    39
3    73    26    33    99    62    72    73     3    55    82
4    72    17    73    71    42    96    56    29    40    96
5    16    10    49     5    61    87    23     7    36     1

# get specific columns with specific rows
>>> df2.loc[2:4, ['col1', 'col5', 'col8']]


Extracting rows and columns based on conditional match

import pandas as pd
# dataframe from dict
df = pd.DataFrame({'col1':['A', 'A', 'C', 'D', 'E'], 'col2':[1,2,3,4,5], 'col3':[0.1,0.2,0.3,0.4,0.5]})
df
# output
  col1  col2  col3
0    A     1   0.1
1    A     2   0.2
2    C     3   0.3
3    D     4   0.4
4    E     5   0.5

# extract rows where col1 has values A 
df.loc[df['col1'] == 'A']
# output
  col1  col2  col3
0    A     1   0.1
1    A     2   0.2

# extract rows where col1 has values A and D
df.loc[df['col1'].isin(['A', 'D'])]
# output
  col1  col2  col3
0    A     1   0.1
1    A     2   0.2
3    D     4   0.4

Statistics of dataframes

# check above to create dataframe objects (df1, df2 and df1_1)
# get descriptive statistics (count, mean, std dev, min, max, and quartiles)
# it excludes all NaN or missing values
>>> df1.describe()
           col2      col3
count  5.000000  5.000000
mean   3.000000  0.300000
std    1.581139  0.158114
min    1.000000  0.100000
25%    2.000000  0.200000
50%    3.000000  0.300000
75%    4.000000  0.400000
max    5.000000  0.500000

# get only certain element like mean ,count
# get mean
>>> df1.describe().loc['mean']
col2    3.0
col3    0.3
Name: mean, dtype: float64

# get mean and std dev
>>> df1.describe().loc[['mean', 'std']]
          col2      col3
mean  3.000000  0.300000
std   1.581139  0.158114

# get standard error of mean (sem)
>>> df1.sem()
col2    0.707107
col3    0.070711
dtype: float64

# for specific columns only
>>> df2['col4'].describe()
count    1000.000000
mean       48.917000
std        28.679865
min         0.000000
25%        25.000000
50%        49.000000
75%        74.000000
max        99.000000
Name: col4, dtype: float64

# for only selected multiple columns
>>> df2.loc[:,'col1':'col5'].describe()
              col1         col2         col3         col4         col5
count  1000.000000  1000.000000  1000.000000  1000.000000  1000.000000
mean     49.334000    51.057000    48.676000    48.917000    48.486000
std      28.311375    29.141293    28.252065    28.679865    29.070619
min       0.000000     0.000000     0.000000     0.000000     0.000000
25%      25.000000    26.000000    25.000000    25.000000    22.000000
50%      50.000000    52.000000    49.000000    49.000000    50.000000
75%      73.000000    77.000000    73.000000    74.000000    73.000000
max      99.000000    99.000000    99.000000    99.000000    99.000000

# to get descriptive statistics by row-wise you can transpose the columns
# or use apply function
>>> df1_1.apply(pd.DataFrame.describe, axis=1)
      count  mean       std  min    25%   50%    75%  max
col1
A       2.0  0.55  0.636396  0.1  0.325  0.55  0.775  1.0
B       2.0  1.10  1.272792  0.2  0.650  1.10  1.550  2.0
C       2.0  1.65  1.909188  0.3  0.975  1.65  2.325  3.0
D       2.0  2.20  2.545584  0.4  1.300  2.20  3.100  4.0
E       2.0  2.75  3.181981  0.5  1.625  2.75  3.875  5.0

# to get descriptive statistics by row-wise but only for specific columns
>> df2.loc[:,'col0':'col4'].apply(pd.DataFrame.describe, axis=1)
     count  mean        std   min   25%   50%   75%   max
0      5.0  43.8  37.325594  12.0  12.0  30.0  69.0  96.0
1      5.0  52.8  34.687173   1.0  43.0  55.0  71.0  94.0
2      5.0  61.6  36.287739  14.0  34.0  72.0  90.0  98.0
3      5.0  70.0  32.992423  21.0  52.0  83.0  96.0  98.0
4      5.0  43.4  34.681407   3.0   9.0  59.0  73.0  73.0
..

Add/Insert rows and columns to existing dataframes

# check above to create dataframe objects (df1, df2 and df1_1)
# add extra row to existing df1 dataframe
>>>df1_2 = df1.append({'col1': 'F', 'col2': 6, 'col3': 0.6}, ignore_index=True)
# ignore_index option will ignore the current index and append to df1
# dataframe.This option is useful when appending two dataframe and to
# preserve the original index of each dataframes
>>> df1_2
  col1  col2  col3
0    A     1   0.1
1    B     2   0.2
2    C     3   0.3
3    D     4   0.4
4    E     5   0.5
5    F     6   0.6

# adding a column to dataframe
>>> df1_2['col4'] = [1, 4, 9, 16, 25,36]
>>> df1_2
  col1  col2  col3  col4
0    A     1   0.1     1
1    B     2   0.2     4
2    C     3   0.3     9
3    D     4   0.4    16
4    E     5   0.5    25
5    F     6   0.6    36

# merge/append two dataframes
# create a new dataframe 
>>> df10 = pd.DataFrame({'col1':['X', 'Y'], 'col2':[7,8], 'col3':[0.7,0.8]})
>>> df10
  col1  col2  col3
0    X     7   0.7
1    Y     8   0.8

# mereg df10 with df1_2
>>> df1_2.append(df10)
  col1  col2  col3  col4
0    A     1   0.1   1.0
1    B     2   0.2   4.0
2    C     3   0.3   9.0
3    D     4   0.4  16.0
4    E     5   0.5  25.0
5    F     6   0.6  36.0
0    X     7   0.7   NaN
1    Y     8   0.8   NaN

# if you set ignore_index=True, the index order will not be preserved
>>> df1_2.append(df10, ignore_index=True)
  col1  col2  col3  col4
0    A     1   0.1   1.0
1    B     2   0.2   4.0
2    C     3   0.3   9.0
3    D     4   0.4  16.0
4    E     5   0.5  25.0
5    F     6   0.6  36.0
6    X     7   0.7   NaN
7    Y     8   0.8   NaN

Export dataframes to file

# check above to create dataframe objects (df1)
# Export to CSV file
# with first column as index
>>> df1.to_csv("df1.csv")
# do not export index as first column
>>> df1.to_csv("df1.csv", index=False)
# Export to TAB file
>>> df1.to_csv("df1.csv", sep='\t')
# no column headers
>>> df1.to_csv("df1.csv", header=False)
# compress output file (works with pandas 1.0.0)
>>> df1.to_csv("df1.zip", compression={'method':'zip', 'archive_name':'df1.csv'})
# export to excel file
# use similar option as above for headers and index
>>> df1.to_excel("df1.xlsx")

Convert series to dataframes

# create pandas series
>>> df11 = pd.Series([1,2,3])
>>> df11
0    1
1    2
2    3
dtype: int64

# convert to dataframe
>>> df11.to_frame()
   0
0  1
1  2
2  3

# add column name
>>> df11.to_frame('col1')
   col1
0     1
1     2
2     3

Convert numpy arrays to dataframes

>>> import numpy as np
>>> x = np.array([[ 1,  2], [ 3 ,4]])

# convert to dataframe
df12 = pd.DataFrame({'col1':x[:,0], 'col2':x[:,1]})
>>> df12
   col1  col2
0     1     2
1     3     4

Convert dataframe to numpy arrays

# see df12 dataframe in above section
# convert whole dataframe to numpy array
>>> df12.to_numpy()
array([[1, 2],
       [3, 4]])

# extract specific column as numpy array
>>> df12['col1'].to_numpy()
array([1, 3])

# extract multiple columns as numpy array
>>> df12[['col1','col2']].to_numpy()
array([[1, 2],
       [3, 4]])

# extract specific row as numpy array
>>> df12.loc[0].to_numpy()
array([1, 2])

# extract multiple row as numpy array
>>> df12.loc[0:1].to_numpy()
array([[1, 2],
       [3, 4]])

Find min and max values in dataframe

Create a dataframe with some random numbers

>>> import pandas as pd
>>> import numpy as np
>>> df = pd.read_csv("https://reneshbedre.github.io/assets/posts/anova/onewayanova.txt", sep="\t")
>>> df.head()
    A   B   C   D
0  25  45  30  54
1  30  55  29  60
2  28  29  33  51
3  36  56  37  62
4  29  40  27  73

# find min in every column
>>> df.min()
A    25
B    29
C    27
D    51
dtype: int64

# find max in every column
>>> df.max()
A    36
B    56
C    37
D    73
dtype: int64

# find min and max in every rows
>>> df.min(axis=1)
>>> df.max(axis=1)

# find max and min from whole dataframe
>>> np.min(df.min().to_numpy())
25
>>> np.max(df.max().to_numpy())
73

# find min and max in particular columns or rows
# max in column A
>>> df['A'].max()
36
# max in row 2
>>> df.loc[1].max()
60
# multiple columns
>>> df[['A', 'D']].max()
A    36
D    73
dtype: int64
# multiple rows (0 and 1 row)
>>> df.loc[0:1,].max(axis=1)
0    54
1    60
dtype: int64

# get row index of max and min values for each column
>>> df.idxmax()
A    3
B    3
C    3
D    4
dtype: int64
# for min df.idxmin()

# to skip missing values (NaN),  default skipna is True
# to not to skip missing values add skipna=False
>>> df.min(skipna=True)


Modifying dataframe columns and column names

import pandas as pd
df = pd.read_csv("https://reneshbedre.github.io/assets/posts/anova/onewayanova.txt", sep="\t")
df.head()
    A   B   C   D
0  25  45  30  54
1  30  55  29  60
2  28  29  33  51
3  36  56  37  62
4  29  40  27  73

# check column names
df.columns
Index(['A', 'B', 'C', 'D'], dtype='object')

# change column names
df.columns = ['A1', 'B1', 'C1', 'D1']
df.head()
    A1 	B1 	C1 	D1
0 	25 	45 	30 	54
1 	30 	55 	29 	60
2 	28 	29 	33 	51
3 	36 	56 	37 	62
4 	29 	40 	27 	73

# reorder column names
df1 = df[['B1', 'A1', 'C1', 'D1']]
# alternate way: df1 = df.reindex(columns=['B1', 'A1', 'C1', 'D1'])
df1
   B1  A1  C1  D1
0  45  25  30  54
1  55  30  29  60
2  29  28  33  51
3  56  36  37  62
4  40  29  27  73

# add new column
df['E']=[55,58,61,70,85]
df.head()
    A1 	B1 	C1 	D1 	E
0 	25 	45 	30 	54 	55
1 	30 	55 	29 	60 	58
2 	28 	29 	33 	51 	61
3 	36 	56 	37 	62 	70
4 	29 	40 	27 	73 	85

# drop column D1 and E
# axis=1 refers to column labels (use 0 for index labels)
df.drop(['D1', 'E'], axis=1)
    A1 	B1 	C1
0 	25 	45 	30
1 	30 	55 	29
2 	28 	29 	33
3 	36 	56 	37
4 	29 	40 	27

# rename specific columns
df.rename(columns={"A1": "a1", "B1": "b1"})
    a1 	b1 	C1 	D1 	E
0 	25 	45 	30 	54 	55
1 	30 	55 	29 	60 	58
2 	28 	29 	33 	51 	61
3 	36 	56 	37 	62 	70
4 	29 	40 	27 	73 	85

join two dataframes (left, right, outer and inner joins)

>>> import pandas as pd
>>> df1 = pd.DataFrame({'col1':['A', 'B', 'C', 'D', 'E', 'F', 'I'], 'col2':[1,2,3,4,5,6,8] })
>>> df1
  col1  col2
0    A     1
1    B     2
2    C     3
3    D     4
4    E     5
5    F     6
6    I     8
>>> df2 = pd.DataFrame({'col1':['A', 'B', 'C', 'D', 'E', 'G'], 'col3':[0.1,0.2,0.3,0.4,0.5,0.7] })
>>> df2
  col1  col3
0    A   0.1
1    B   0.2
2    C   0.3
3    D   0.4
4    E   0.5
5    G   0.7

# left join df1 and df2 (join based on all keys from df1)
>>> df_left_joined = pd.merge(df1, df2, how='left', on='col1')
>>> df_left_joined
  col1  col2  col3
0    A     1   0.1
1    B     2   0.2
2    C     3   0.3
3    D     4   0.4
4    E     5   0.5
5    F     6   NaN
6    I     8   NaN

# right join df1 and df2 (join based on all keys from df2)
>>> df_right_joined = pd.merge(df1, df2, how='right', on='col1')
>>> df_right_joined
  col1  col2  col3
0    A   1.0   0.1
1    B   2.0   0.2
2    C   3.0   0.3
3    D   4.0   0.4
4    E   5.0   0.5
5    G   NaN   0.7

# outer join df1 and df2 (join based on union of keys of df1 and df2)
>>> df_outer_joined = pd.merge(df1, df2, how='outer', on='col1')
>>> df_outer_joined
  col1  col2  col3
0    A   1.0   0.1
1    B   2.0   0.2
2    C   3.0   0.3
3    D   4.0   0.4
4    E   5.0   0.5
5    F   6.0   NaN
6    I   8.0   NaN
7    G   NaN   0.7

# inner join df1 and df2 (join based on common keys of df1 and df2)
>>> df_inner_joined = pd.merge(df1, df2, how='inner', on='col1')
>>> df_inner_joined
  col1  col2  col3
0    A     1   0.1
1    B     2   0.2
2    C     3   0.3
3    D     4   0.4
4    E     5   0.5

Correlation analysis with pandas dataframe

>>> import pandas as pd
>>> df = pd.read_csv("https://reneshbedre.github.io/assets/posts/anova/onewayanova.txt", sep="\t")
>>> df.head()
    A   B   C   D
0  25  45  30  54
1  30  55  29  60
2  28  29  33  51
3  36  56  37  62
4  29  40  27  73

# perform pearson correlation analysis
# check more methods at https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.corr.html
# returns correlation coefficient matrix
>>> df.corr(method='pearson')
          A         B         C         D
A  1.000000  0.569327  0.657544  0.341802
B  0.569327  1.000000  0.194615  0.264710
C  0.657544  0.194615  1.000000 -0.391609
D  0.341802  0.264710 -0.391609  1.000000

Compare all rows or columns

>>> import pandas as pd
>>> df = pd.DataFrame({'col1':['0', '0', '0' ], 'col2':[0,2,3], 'col3':[0,0.2,0.3]})
>>> df
  col1  col2  col3
0    0     0   0.0
1    0     2   0.2
2    0     3   0.3

# delete all rows where all columns are zero
>>> df[(df!=0).all(axis=1)]
  col1  col2  col3
1    0     2   0.2
2    0     3   0.3

# get all rows where columns sum > 3
>>> df[(df.sum(axis=1)>3)]
  col1  col2  col3
2    0     3   0.3

Last updated: November 09, 2020