python:pandas dataframe數據處理總結,讀寫文件,增刪改查,缺失處理,連接合並,去重


  這篇blog長期更新,總結一些python數據處理過程中常用的方法(不總結老是忘),我一般喜歡用dataframe做數據處理,所以一般會盡量轉成pandas的dataframe格式。所有方法需要先導入庫pandas和numpy。

import pandas as pd
import numpy as np

1. 生成dataframe

  pd.DataFrame

# 輸入二維向量,columns是列索引,index是行索引
df = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]], columns=['a', 'b', 'c'], index = [0,1,2])
print(df)

# 輸出:
"""
   a  b  c
0  1  2  3
1  4  5  6
2  7  8  9
"""

更改行索引的顯示順序:

  df.reindex

df = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]], columns=['a', 'b', 'c'], index = [0,1,2])
df = df.reindex([2,1,0])
print(df)

# 輸出
"""
   a  b  c
2  7  8  9
1  4  5  6
0  1  2  3
"""

更新列索引名:

  df.columns
  df.rename

df = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]], columns=['a', 'b', 'c'], index = [0,1,2])
# 法一,暴力法
df.columns=['a', 'b', 'f']
# 法二,當列索引的數量很多,用第二種比較好,可以只改一部分
df.rename(columns={'b':'e'}, inplace = True)
print(df)

# 輸出
"""
   a  e  f
0  1  2  3
1  4  5  6
2  7  8  9
"""

2. 讀寫數據文件

json:

# 讀取方法一,針對利用pd.to_json()存的json十分合適
pd.read_json('xxx')
# 讀取方法二,有時候讀取的json文件,不是由pd.to_json存的,利用pd.read_csv可能會有問題,要用這種方法
with open('xxx') as f:
    for i in f.readlines():
        data = json.load(i)
        
# 寫方法
pd.to_json('xxx')

csv:


# 讀方法,nrows用於選擇讀取前多少行,usecols用於選擇讀取哪些列
df = pd.read_csv('xxx.csv', nrows = 10000, usecols = [0, 5, 82, 83])

# 寫方法,ignore_index用於確定是否保存行索引,ignore_index=False會保存行索引
df.to_csv('xxx.csv', ignore_index=True)

txt:

# 讀取
data=[]
f = open('xxx.txt', 'r')
for line in f:
    info1, info2 = line.split(',')    # 這裏默認一行只有一個","來分割兩個數據,可以視情況增加輸出 
    data.append([info1, info2])
f.close

# 一般不建議保存成txt,可以直接保存成csv

3. 增加:

增加一列:

df = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]], columns=['a', 'b', 'c'], index = [0,1,2])
df['d']=[4,7,10]
print(df)

# 輸出
"""
   a  b  c   d
0  1  2  3   4
1  4  5  6   7
2  7  8  9  10
"""

  如果增加一列,這一列是用已有的列計算出來的,注意如果原來的df中有’d’這一列,則會把數據替換掉。
  df.assign

df = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]], columns=['a', 'b', 'c'], index = [0,1,2])
df = df.assign(d = lambda x: x.a+x.b)   # a索引列與b索引列相加
print(df)

# 輸出
"""
   a  b  c   d
0  1  2  3   3
1  4  5  6   9
2  7  8  9  15
"""

  df.assign是一個十分強大的函數,下面給出一個更復雜的情況,把df的’a’列的元素向下移動一列放到’d’列中。

df = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]], columns=['a', 'b', 'c'])
df.assign(d = lambda x: x.b.shift(1))

# 輸出
"""
   a  b  c    d
0  1  2  3  NaN
1  4  5  6  2.0
2  7  8  9  5.0
"""

  指定位置增加一列

df = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]], columns=['a', 'b', 'c'], index = [0,1,2])
df.insert(1,'B',[11,12,13])     #列索引的1位置插入一列
print(df)

# 輸出
"""
   a   B  b  c
0  1  11  2  3
1  4  12  5  6
2  7  13  8  9
"""

增加一行:

  df.append

df = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]], columns=['a', 'b', 'c'], index = [0,1,2])
df = df.append({'a':10, 'b':11, 'c':12}, ignore_index=True)
print(df)

# 輸出
"""
    a   b   c
0   1   2   3
1   4   5   6
2   7   8   9
3  10  11  12
"""

4. 刪除:

  df.drop

df = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]], columns=['a', 'b', 'c'], index = [0,1,2])
df.drop('a',axis=1, inplace=True)     # axis=1刪除列,inplace爲True表示直接對原表修改且返回None,默認是False
df = df.drop(0,axis=0)     # axis=0刪除行
print(df)

# 輸出
"""
   b  c
1  5  6
2  8  9
"""

5. 更新

shuffle

from sklearn.utils import shuffle  
df = shuffle(df)  
# shuffle對list也是可以的

排序

對行排序

  df.sort_values

df = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]], columns=['a', 'b', 'c'], index = [0,1,2])
df = df.sort_values('b', ascending=False)      # ascending=False表示降序排列
print(df)

# 輸出
"""
   a  b  c
2  7  8  9
1  4  5  6
0  1  2  3
"""

  也可以根據多個元素進行排序,寫前面的優先考慮

df = pd.DataFrame([[1,2,3],[1,5,6],[2,5,9],[2,2,12]], columns=['a', 'b', 'c'], index = [0,1,2,3])
df = df.sort_values(['a', 'b'], ascending=False)    # 先按照a列降序排,在a的基礎上,考慮b列的降序排
print(df)

# 輸出
"""
   a  b   c
2  2  5   9
3  2  2  12
1  1  5   6
0  1  2   3
"""

  值得一提的是,這個排序方式也可以排序時間格式類似’2018-10-01 08:00:00’的時間數據。

對列排序(自定義列的順序)

data = pd.DataFrame([[1,'a','1a'],[2,'b','2b'],[3, 'c', '3c']], columns=['col1', 'col2', 'col3'])
print(data)
# data
"""
   col1 col2 col3
0     1    a   1a
1     2    b   2b
2     3    c   3c
"""

reorder = ['col3', 'col2', 'col1']
data = data[reorder]
print(data)
# 按列重排後data
"""
  col3 col2  col1
0   1a    a     1
1   2b    b     2
2   3c    c     3
"""

分組

  df.groupby():分組,分組後輸出的是一個類,需要用for循環讀出。

df = pd.DataFrame([[1,2,3],[1,5,6],[7,8,9],[7,11,12]], columns=['a', 'b', 'c'])
for index, data in df.groupby(['a']):
    print(index)  # index是分組標籤值
    print(data)  # data是該分組下的dataframe數據
    
# 輸出
"""
1
   a  b  c
0  1  2  3
1  1  5  6
7
   a   b   c
2  7   8   9
3  7  11  12
"""

  當然也可以進行多列分組,只需向groupby()傳入一個含有多個元素的列表即可,同排序類似,寫在前面的優先級高。
  一般df.groupby()後,會接一個函數,例如count(),sum()等。

df = pd.DataFrame([[1,2,3],[1,5,6],[7,8,9],[7,11,12]], columns=['a', 'b', 'c'])
print(df.groupby(['a'], as_index=False).count())

# 輸出
"""
   a  b  c
0  1  2  2     # b,c列的元素,表示分到a的每一個分組中的記錄個數
1  7  2  2
"""
df = pd.DataFrame([[1,2,3],[1,5,6],[7,8,9],[7,11,12]], columns=['a', 'b', 'c'])
print(df.groupby(['a'], as_index=False).sum())

# 輸出
"""
   a  b  c
   a   b   c
0  1   7   9   # b,c列的元素,表示分到a的每一個分組中的記錄,對應的b,c值的和
1  7  19  21
"""

聚合

  df.agg()一般在分組groupby()後進行,可以把我們想要觀察的指標,寫入一個列表[‘min’, ‘mean’, ‘max’, n0],n0是自定義的,出入agg()。

def n0(x): return sum(x==8) # 計算等於8的個數
df = pd.DataFrame([[1,2,3],[1,5,6],[7,8,9],[7,11,12]], columns=['a', 'b', 'c'])
print(df.groupby(['a'], as_index=False).agg(['min', 'mean', 'max', n0]))

# 輸出
"""
    b               c             
  min mean max n0 min  mean max n0
a                                 
1   2  3.5   5  0   3   4.5   6  0                     # 在a=1的分組中,b,c的最小值、平均值、最大值、等於8的個數
7   8  9.5  11  1   9  10.5  12  0
"""

行索引變成外層索引,列索引變成內層索引

  df.stack()

df = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]], columns=['a', 'b', 'c'], index = [0,1,2])
df.loc[1,'b'] = 'a'
df.stack()

# 輸出
"""
0  a    1
   b    2
   c    3
1  a    4
   b    a
   c    6
2  a    7
   b    8
   c    9
dtype: object
"""

更新某個值

  df.loc

df = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]], columns=['a', 'b', 'c'], index = [0,1,2])
df.loc[1,'b'] = 'a'
print(df)

# 輸出
"""
   a  b  c
0  1  2  3
1  4  a  6
2  7  8  9
"""

行索引重置

  df.reset_index(drop=True)

df = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]], columns=['a', 'b', 'c'], index=[1,2,3])
df = df.reset_index(drop=True)
print(df) 

# 輸出
"""
   a  b  c
0  1  2  3
1  4  5  6
2  7  8  9
"""

6. 顯示:

顯示一列

df = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]], columns=['a', 'b', 'c'])

# 法一 輸出series
print(df['a'])    # series
# 輸出
"""
0    1
1    4
2    7
Name: a, dtype: int64
"""

# 法二 輸出dataframe
print(df[['a']]) 
"""
   a
0  1
1  4
2  7
"""

# 法三
print(df.a)
# 輸出
"""
0    1
1    4
2    7
Name: a, dtype: int64
"""

顯示多列

df = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]], columns=['a', 'b', 'c'])
print(df[['a', 'b']])

# 輸出
"""
   a  b
0  1  2
1  4  5
2  7  8
"""

顯示一行:

df = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]], columns=['a', 'b', 'c'])
print(df.loc[0]) 

# 輸出
"""
a    1
b    2
c    3
Name: 0, dtype: int64
"""

顯示某個元素:

df = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]], columns=['a', 'b', 'c'])
print(df.loc[1, 'b']) 

# 輸出
"""
5
"""

暴力法:

  利用df.iloc[],把dataframe完全當做二維ndarray進行索引,切片。

df = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]], columns=['a', 'b', 'c'])
print(df.iloc[1, :]) 

# 輸出
"""
a    4
b    5
c    6
Name: 1, dtype: int64
"""

關於list與ndarray索引與切片的補充

  注意二維list是不能進行二維切片,例如list[1, :]這樣的切片,二維ndarray是可以的,因爲list的一個[]中不能存在多維索引,ndarray可以,具體看下面的例子。

a = [[1,2,3],[4,5,6]]
np_a = np.asarray(a)

a[1][1]        # 可以
np_a[1][1]     # 可以
a[1,1]         # 報錯
np_a[1,1]      # 可以,同np_a[1][1] 
a[1][:]        # 可以
np_a[1][:]     # 可以
a[1, :]        # 報錯
np_a[1, :]     # 可以,同np_a[1][:] 

查看某一行或者某一列有多少元素是1

df = pd.DataFrame([[1,2,3],[1,5,6],[2,5,9],[2,2,12]], columns=['a', 'b', 'c'], index = [0,1,2,3])
def n0(x): return sum(x==1)   # 計算等於1的個數
df.apply(n0, axis=0)   # 對行或者列執行n0,axis=0是按列,有點像spark那樣一個一個的計算

# 輸出
"""
a    2
b    0
c    0
dtype: int64
"""

條件篩選:

df = pd.DataFrame([[1,2,3],[4,5,6],[7,5,6],[7,8,9]], columns=['a', 'b', 'c'], index = [0,1,2,3])
print(df[df['b']==5])

# 輸出
"""
   a  b  c
1  4  5  6
2  7  5  6
"""

如果要同時滿足多個條件,要用&鏈接,同時加上(),不能用and鏈接,不然會報錯:ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

df = pd.DataFrame([[1,2,3],[4,5,6],[7,5,6],[7,8,9]], columns=['a', 'b', 'c'], index = [0,1,2,3])
print(df[(df['b']==5) & (df['a']==4)])

# 輸出
"""
   a  b  c
1  4  5  6
"""

找出某一列的最大值的行列索引

df = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]], columns=['a', 'b', 'c'])
print(df.loc[lambda x: x.a==max(x.a),'a']) 

# 輸出
"""
2    7
Name: a, dtype: int64
"""

可以把lambda中的x就理解爲df自己,和spark中的用法不太一樣,spark的lambda中的x一般理解爲一行。

7. 數據缺失的處理:

  注意,缺失值的表示是np.nan(或np.NaN),不是None。np.nan是沒有可比性的,看下面的代碼

print(np.nan == np.nan)  # 輸出False

  想要單獨判斷np.nan,需要使用方法np.isnan()

np.isnan(np.NaN)  # 輸出True

  但是None是有可比性的

print(None == None)  # 輸出True

  如果利用pd.read_csv等讀取數據,存在缺失值時,會使用np.nan填充的,我們可以利用下面的方法進行缺失值判斷。

df = pd.DataFrame([[1,2,3],[np.NaN,5,6],[7,None,9]], columns=['a', 'b', 'c'])
print(df.isnull()) 

# 輸出
"""
       a      b      c
0  False  False  False
1   True  False  False
2  False   True  False
"""

  可以看出來,其實np.nan與None都會被當做缺失值處理的,但實際兩者是不一樣的。也可以單獨對某一列處理。

df = pd.DataFrame([[1,2,3],[np.NaN,5,6],[7,None,9]], columns=['a', 'b', 'c'])
print(df['a'].isnull()) 
print()
print(pd.isnull(df['a']))   # 與第二行一樣的效果

# 輸出
"""
0    False
1     True
2    False
Name: a, dtype: bool

0    False
1     True
2    False
Name: a, dtype: bool
"""

  如果在一行裏存在多個特徵缺失,我們想得到有缺失值的行的索引,可以用如下方法

df = pd.DataFrame([[1,2,3,4],[5,np.NaN,7,8],[9,np.NaN,11,np.NaN],[13,np.NaN,np.NaN,np.NaN]], columns=['a', 'b', 'c','d'])
print(df)
# 輸出
"""
    a    b     c    d
0   1  2.0   3.0  4.0
1   5  NaN   7.0  8.0
2   9  NaN  11.0  NaN
3  13  NaN   NaN  NaN
"""

df_isnull = df[df.isnull().values==True]   #顯示存在缺失值的行列
print(df_isnull)
# 輸出,可以看出,這種方法會重複計算,如果一行中有3個缺失值,就會選出3次,這是重複的篩選
"""
    a   b     c    d
1   5 NaN   7.0  8.0
2   9 NaN  11.0  NaN
2   9 NaN  11.0  NaN
3  13 NaN   NaN  NaN
3  13 NaN   NaN  NaN
3  13 NaN   NaN  NaN
"""

print(df_isnull.drop_duplicates())  #去除一行中有n個缺失值的重複列,返回缺失的數據量
# 輸出,把重複的去除即可
"""
    a   b     c    d
1   5 NaN   7.0  8.0
2   9 NaN  11.0  NaN
3  13 NaN   NaN  NaN
"""

print(df_isnull.drop_duplicates().index)  #拿到存在缺失值的索引
"""
Int64Index([1, 2, 3], dtype='int64')
"""

  我們可以用下面的方法對缺失值進行處理,仍然是同時處理np.nan與None。

# 刪掉含有缺失值的記錄
df = pd.DataFrame([[1,2,3],[np.NaN,5,6],[7,None,9]], columns=['a', 'b', 'c'])
print(df.dropna())

# 輸出
"""
     a    b  c
0  1.0  2.0  3
"""
# 填補0
df = pd.DataFrame([[1,2,3],[np.NaN,5,6],[7,None,9]], columns=['a', 'b', 'c'])
print(df.fillna(0))

# 輸出
"""
     a    b  c
0  1.0  2.0  3
1  0.0  5.0  6
2  7.0  0.0  9
"""
# 填補missing字樣
df = pd.DataFrame([[1,2,3],[np.NaN,5,6],[7,None,9]], columns=['a', 'b', 'c'])
print(df.fillna('missing')) 

# 輸出
"""
         a        b  c
0        1        2  3
1  missing        5  6
2        7  missing  9
"""
# 填補上一個數字
df = pd.DataFrame([[1,2,3],[np.NaN,5,6],[7,None,9]], columns=['a', 'b', 'c'])
print(df.fillna(method='pad'))

# 輸出
"""
     a    b  c
0  1.0  2.0  3
1  1.0  5.0  6
2  7.0  5.0  9
"""

更多的方法可以看官方文檔:http://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.fillna.html

  當然,對於不同的情況,我們也許會設計不同的方法進行填充,甚至是特別複雜的方法。

8. 數據連接與合併:

連接

  pd.merge的功能同join差不多,可以實現內連接,左外鏈接,右外連接和全連接,對應的參數how={‘inner’, ‘left’, ‘right’, ‘outer’}。

df = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]], columns=['a', 'b', 'c'])
df2 = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]], columns=['a', 'd', 'f'])
output = pd.merge(df, df2, on='a', how='left')    # df是左,df2是右
print(output) 

# 輸出
"""
   a  b  c  d  f
0  1  2  3  2  3
1  4  5  6  5  6
2  7  8  9  8  9
"""

  如果df與df2的用於連接的字段名字不一樣

df = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]], columns=['a', 'b', 'c'])
df2 = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]], columns=['d', 'e', 'f'])
output = pd.merge(df, df2, left_on='a', right_on='d', how='left').drop('d', axis=1)  # 需要手動刪除重複的列
print(output)

# 輸出
"""
   a  b  c  e  f
0  1  2  3  2  3
1  4  5  6  5  6
2  7  8  9  8  9
"""

  如果右邊出現用於連接的字段值重複的情況,會多次鏈接,以左鏈接爲例,如下

df = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]], columns=['a', 'b', 'c'])
df2 = pd.DataFrame([[1,2,3],[1,5,6],[7,8,9]], columns=['d', 'e', 'f'])
output = pd.merge(df, df2, left_on='a', right_on='d', how='left')   # 這裏沒有刪除d,所以下面會顯示d列
print(output) 

# 輸出
"""
   a  b  c    d    e    f
0  1  2  3  1.0  2.0  3.0
1  1  2  3  1.0  5.0  6.0
2  4  5  6  NaN  NaN  NaN
3  7  8  9  7.0  8.0  9.0
"""

  所以在進行連接前,要構思好邏輯,可以加去重。

合併

  df2拼到df下面

df = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]], columns=['a', 'b', 'c'])
df2 = pd.DataFrame([[1,2,3],[1,5,6],[7,8,9]], columns=['a', 'b', 'c'])
output = pd.concat([df, df2], ignore_index=True, axis=0)  # axis=0,df2拼到df下面,ignore_index=True即把index重排
print(output) 

# 輸出
"""
  a  b  c
0  1  2  3
1  4  5  6
2  7  8  9
3  1  2  3
4  1  5  6
5  7  8  9
"""

  df2拼到df右面

df = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]], columns=['a', 'b', 'c'])
df2 = pd.DataFrame([[1,2,3],[1,5,6],[7,8,9]], columns=['a', 'b', 'c'])
output = pd.concat([df, df2], ignore_index=True, axis=1)   # ;axis=1,df2拼到df右面
print(output) 

# 輸出
"""
   0  1  2  3  4  5
0  1  2  3  1  2  3
1  4  5  6  1  5  6
2  7  8  9  7  8  9
"""

9. 去重複

  df.drop_duplicates().reset_index(drop=True)

df = pd.DataFrame([[1,2,3],[1,2,3],[7,8,9]], columns=['a', 'b', 'c'])
df = df.drop_duplicates().reset_index(drop=True)
print(df) 

# 輸出
"""
   a  b  c
0  1  2  3
1  7  8  9
"""

10.其它

提取數據,dataframe轉list

  df.values

df = pd.DataFrame([[1,2,3],[1,2,3],[7,8,9]], columns=['a', 'b', 'c'])
list_df = df.values
print(list_df)

# 輸出
"""
[[1 2 3]
 [1 2 3]
 [7 8 9]]
"""

提取行索引

  df.index

df = pd.DataFrame([[1,2,3],[1,2,3],[7,8,9]], columns=['a', 'b', 'c'])
for index in df.index:
    print(index)

# 輸出
"""
0
1
2
"""

提取列索引

  df.columns

df = pd.DataFrame([[1,2,3],[1,2,3],[7,8,9]], columns=['a', 'b', 'c'])
for index in df.columns:
    print(index)

# 輸出
"""
a
b
c
"""
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章