Extracting specific selected columns to new DataFrame as a copy
Asked Answered
W

10

363

I have a pandas DataFrame with 4 columns and I want to create a new DataFrame that only has three of the columns. This question is similar to: Extracting specific columns from a data frame but for pandas not R. The following code does not work, raises an error, and is certainly not the pandas way to do it.

import pandas as pd
old = pd.DataFrame({'A' : [4,5], 'B' : [10,20], 'C' : [100,50], 'D' : [-30,-50]})
new = pd.DataFrame(zip(old.A, old.C, old.D)) 
# raises TypeError: data argument can't be an iterator 

What is the pandas way to do it?

Wellchosen answered 8/1, 2016 at 17:34 Comment(1)
I would like to see a solution that works also if two columns are the same. For example: A, B, B..Mulcahy
C
683

There is a way of doing this and it actually looks similar to R

new = old[['A', 'C', 'D']].copy()

Here you are just selecting the columns you want from the original data frame and creating a variable for those. If you want to modify the new dataframe at all you'll probably want to use .copy() to avoid a SettingWithCopyWarning.

An alternative method is to use filter which will create a copy by default:

new = old.filter(['A','B','D'], axis=1)

Finally, depending on the number of columns in your original dataframe, it might be more succinct to express this using a drop (this will also create a copy by default):

new = old.drop('B', axis=1)
Cullan answered 8/1, 2016 at 17:51 Comment(2)
A caution if just copying one column: In old[['A']].copy(), the double square brackets are required to create a new data frame. Note that old['A'].copy() will only create a Series.Inseverable
Thank you for this amazing explanation. I was having a lot of trouble with creating new columns (after creating a new dataframe without the copy method). Stumped me. You answer finally helped me get to the bottom of it.Corwin
V
62

The easiest way is

new = old[['A','C','D']]

.

Vice answered 11/6, 2019 at 18:9 Comment(4)
This isn't making a copy unless you explicitly call .copy()Hartle
this copies by default.Negation
@Nguaial the behaviour of simple indexing is not specified. You will not know if you get a copy or a view. See documentation for more details: pandas.pydata.org/pandas-docs/stable/user_guide/…Buckwheat
As mentioned in the comment above, this will create a view and not a copy.Graybeard
I
22

Another simpler way seems to be:

new = pd.DataFrame([old.A, old.B, old.C]).transpose()

where old.column_name will give you a series. Make a list of all the column-series you want to retain and pass it to the DataFrame constructor. We need to do a transpose to adjust the shape.

In [14]:pd.DataFrame([old.A, old.B, old.C]).transpose()
Out[14]: 
   A   B    C
0  4  10  100
1  5  20   50
Inhaler answered 15/1, 2019 at 6:50 Comment(4)
works, but not if column_name has special characters.Wendel
oh had not thought of thatInhaler
@Wendel in that case you can do old['column_name'] I believeCheckers
@Checkers yes, but that is not in the solutionWendel
C
16

columns by index:

# selected column index: 1, 6, 7
new = old.iloc[: , [1, 6, 7]].copy() 
Charlatan answered 24/9, 2019 at 9:5 Comment(0)
A
8

As an alternative:

new = pd.DataFrame().assign(A=old['A'], C=old['C'], D=old['D'])
Abscess answered 14/12, 2021 at 14:5 Comment(0)
V
7

As far as I can tell, you don't necessarily need to specify the axis when using the filter function.

new = old.filter(['A','B','D'])

returns the same dataframe as

new = old.filter(['A','B','D'], axis=1)
Viosterol answered 11/6, 2019 at 17:45 Comment(0)
P
6

Generic functional form

def select_columns(data_frame, column_names):
    new_frame = data_frame.loc[:, column_names]
    return new_frame

Specific for your problem above

selected_columns = ['A', 'C', 'D']
new = select_columns(old, selected_columns)
Perception answered 8/4, 2019 at 11:4 Comment(0)
Y
0

If you want to have a new data frame then:

import pandas as pd
old = pd.DataFrame({'A' : [4,5], 'B' : [10,20], 'C' : [100,50], 'D' : [-30,-50]})
new=  old[['A', 'C', 'D']]
Yep answered 24/1, 2020 at 15:41 Comment(2)
Dangerous; this isn't making a copy.Groggy
What's the diffrence between copy and copy of a slice of Dataframe?Zymometer
C
0

You can drop columns in the index:

df = pd.DataFrame({'A': [1, 1], 'B': [2, 2], 'C': [3, 3], 'D': [4, 4]})

df[df.columns.drop(['B', 'C'])]

or

df.loc[:, df.columns.drop(['B', 'C'])]

Output:

   A  D
0  1  4
1  1  4
Chipmunk answered 25/10, 2021 at 23:17 Comment(0)
D
0

You can also use get() to create a new copy (that doesn't run into SettingWithCopyWarning).

new = old.get(['A', 'C', 'D'])

Also, filter selects by column labels by default, so the following works.

new = old.filter(['A', 'C', 'D'])

axis= is needed if one needs to select by row. For example, old.filter([0], axis=0) selects the first row.

If new is an already existing dataframe, then assign() also works (if you want to keep the old columns with their original column names).

new = pd.DataFrame()
new = new.assign(**old[['A', 'C', 'D']])
Diamine answered 16/3, 2023 at 7:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.