1. 程式人生 > >資料分析與AI(五)pandas的資料拼接操作/美國各州人口分析/蘋果歷年股票曲線圖

資料分析與AI(五)pandas的資料拼接操作/美國各州人口分析/蘋果歷年股票曲線圖

pandas的拼接操作

pandas的拼接分為兩種:
- 級聯: pd.concat, pd.append
- 合併: pd.merge, pd.join

0. 回顧numpy的級聯

import numpy as np
import pandas as pd

from pandas import Series, DataFrame
import matplotlib.pyplot as plt
nd = np.random.randint(0,150,size=(5,4))
nd
# 結果如下:
array([[ 54,  65,  70,  31],
       [  5,  85
, 36, 137], [ 87, 38, 63, 77], [ 89, 82, 58, 96], [145, 125, 89, 84]])
# 列級聯
np.concatenate([nd,nd], axis=1)
# 結果如下:
array([[ 54,  65,  70,  31,  54,  65,  70,  31],
       [  5,  85,  36, 137,   5,  85,  36, 137],
       [ 87,  38,  63,  77,  87,  38,  63,  77],
       [ 89
, 82, 58, 96, 89, 82, 58, 96], [145, 125, 89, 84, 145, 125, 89, 84]])

為方便講解,我們首先定義一個生成DataFrame的函式:

def make_df(cols, index):
    data = {col:[str(col)+str(ind) for ind in index] for col in cols}

    df = DataFrame(data= data, columns = cols, index = index)

    return df
df1 = make_df(['a'
,'b','c'],[1,2,3]) df1 # 結果如下:

這裡寫圖片描述

df2 = make_df(['a','b','c'],[4,5,6])
df2
# 結果如下:

這裡寫圖片描述

1. 使用pd.concat()級聯

pandas使用pd.concat函式,與np.concatenate函式類似,只是多了一些引數:

pandas使用pd.concat函式,與np.concatenate函式類似,只是多了一些引數:

pd.concat(objs, axis=0, join='outer', join_axes=None, ignore_index=False,
          keys=None, levels=None, names=None, verify_integrity=False,
          copy=True)

1) 簡單級聯

和np.concatenate一樣,優先增加行數(預設axis=0)

# np.concatenate(axis=1)的情況是水平級聯, np中沒有index, 和columns 所以只要行列相等就可以級聯
# 在pd中, 如果行 和 列 不一致, 但是shape(形狀)相同, 會級聯成一個更大的df, 但是不對應的值會填充NaN
pd.concat([df1, df2], axis=1)
# 結果如下:

這裡寫圖片描述
可以通過設定axis來改變級聯方向

pd.concat([df1, df2], axis=0)
# 結果如下:

這裡寫圖片描述
注意index在級聯時可以重複

df3 = make_df(['a','b','c'],[2,3,4])
df3
# 結果如下

這裡寫圖片描述

pd.concat([df1,df3],axis=1)
# 水平合併以後結果如下:

這裡寫圖片描述
也可以選擇忽略ignore_index,重新索引

# git中有 .gitignore 這個檔案, 會把寫入的檔案路徑給遮蔽, 不會被上傳到github
# add . commit push
# ignore_index 作用是對索引重新排序
pd.concat([df1,df3],axis=0, ignore_index=True)

# 在工作中, 大部分的分析資料來源於mysql,mysql中的id都是唯一的, 分表

# mysql在面試中是最重要的,分表  每個表最大的儲存限制是100w條, 一般只會使用80W條是最優的

這裡寫圖片描述
或者使用多層索引 keys

concat([x,y],keys=[‘x’,’y’])

df4 = pd.concat([df1,df3],keys=['期中','期末'])
df4
# 結果如下:

這裡寫圖片描述

2) 不匹配級聯

不匹配指的是級聯的維度的索引不一致。例如縱向級聯時列索引不一致,橫向級聯時行索引不一致
有3種連線方式:

  • 外連線:補NaN(預設模式)
df1
# 結果如下:

這裡寫圖片描述

df5 = make_df(['c','d','e'],[3,4,5])
df5
# 結果如下:

這裡寫圖片描述

# concat預設使用的外連線
df6 = pd.concat([df1,df5],axis=0)
df6
# 結果如下:

這裡寫圖片描述

  • 內連線:只連線匹配的項
# 回憶mysql中, outer和inner的不同
# 外連線  left 以左邊的表中的資料為核心, 右邊資料不匹配,則填充Null
# 內連線 join 兩邊表資料不完全對應的話, 會只顯示能對應上的資料
# join  預設值是outer
df6 = pd.concat([df1,df5],axis=1, join='inner')
df6

# 同mysql一致

這裡寫圖片描述

  • 連線指定軸 join_axes
df1
# 結果如下:

這裡寫圖片描述

df5
# 結果如下:

這裡寫圖片描述

df7 = pd.concat([df1, df5], join_axes=[df1.columns])
df7

# join_axes 的值 是一個列表[df1.index]
# select df1.a, df1.b , df1.c from df1 left join df5 using(c);
# using(c) 相當於: on df1.c=df5.c

這裡寫圖片描述

3) 使用append()函式新增

由於在後面級聯的使用非常普遍,因此有一個函式append專門用於在後面新增

append 和 concat相似

df1
# 結果如下:

這裡寫圖片描述

df2
# 結果如下:

這裡寫圖片描述

# 垂直
df1.append(df2)

這裡寫圖片描述

2. 使用pd.merge()合併

merge與concat的區別在於,merge需要依據某一共同的行或列來進行合併

使用pd.merge()合併時,會自動根據兩者相同column名稱的那一列,作為key來進行合併。

注意每一列元素的順序不要求一致

1) 一對一合併

df1
# 結果如下:

這裡寫圖片描述

df2
# 結果如下:

這裡寫圖片描述

# 預設是內連線, 表的兩邊資料都不對應
pd.merge(df1,df2)
# 結果如下

這裡寫圖片描述

display(df1,df5)

這裡寫圖片描述

how的取值 : {‘left’, ‘right’, ‘outer’, ‘inner’}, default ‘inner’

pd.merge(df1,df5, how='right')
# 結果如下:

這裡寫圖片描述

pd.merge(df1,df5,how='left')
# 結果如下:

這裡寫圖片描述

2) 多對一|一對多合併

df1
# 結果如下:

這裡寫圖片描述

df8 = make_df(['c','d','e'],[1,1,1,4])
df8
# 結果如下:

這裡寫圖片描述

pd.merge(df1,df8)

# select * from df1 join df8 on df1.c= df8.c

這裡寫圖片描述

pd.merge(df1,df8, how='left')
# 結果如下:

這裡寫圖片描述

pd.merge(df1,df8,how='outer')

# 在工作中用outer , 可以自動分配

這裡寫圖片描述

pd.merge(df1,df8,how='right')
# 結果如下:

這裡寫圖片描述

3) 多對多合併

df8
# 結果如下:

這裡寫圖片描述

df8.iloc[0]['d'] = 'qwe'
df8['e'][4] = 'asd'
df9 = make_df(list('abd'),[1,1,4,4])
df9
# 結果如下;

這裡寫圖片描述

pd.merge(df9, df8)
# 結果如下:

這裡寫圖片描述

pd.merge(df9, df8,how='outer')
# 結果如下:

這裡寫圖片描述

4) key的規範化

  • 使用on=顯式指定哪一列為key,當有多個key相同時使用
df1
# 結果如下:

這裡寫圖片描述

# on的作用是將兩個表中相同資料型別, 含義一致的欄位進行連線的
pd.merge(df1,df10, left_on='c',right_on='w')
# mysql
# select * 

這裡寫圖片描述

df11 = make_df(list('bcd'),[1,2,3])
df11
# 結果如下:

這裡寫圖片描述

pd.merge(df1,df11,on='b')

# mysql中一般碰到兩個欄位相同, 但是代表的含義不一樣, 或者資料型別不同
# a.u = b.u  a.o - b.o
# select a.o a_o, b.o b_o

這裡寫圖片描述

6) 列衝突的解決

當列衝突時,即有多個列名稱相同時,需要使用on=來指定哪一個列作為key,配合suffixes指定衝突列名

可以使用suffixes=自己指定字尾

pd.merge(df1,df11,on='c',suffixes=('_up','_down'))
# 結果如下:

這裡寫圖片描述

U.S.A人口分析

United States America

import pandas as pd
from pandas import DataFrame, Series
# 先匯入資料檔案

# 各州的面積
areas = pd.read_csv('../data/state-areas.csv')
# 縮寫
abbr = pd.read_csv('../data/state-abbrevs.csv')
# 人口
pop = pd.read_csv('../data/state-population.csv')
areas.head()
pop.head()
# 結果如下:

這裡寫圖片描述
這裡寫圖片描述

# 開始合併
abbrToPop = pd.merge(abbr,pop, left_on='abbreviation', right_on='state/region', how='outer')
abbrToPop.head()
# 合併結果如下:

這裡寫圖片描述

# 將重複的列刪除掉
# .drop()
# 一般的一執行完就列印的, 這種形式的方法不對原資料產生影響, inplace代表是否對原陣列產生影響
abbrToPop.drop(columns='abbreviation', inplace=True) # 或者abbrToPop.drop(labels='abbreviation', axis=1)
# 空資料一般會顯示NaN
abbrToPop.isnull().any()
# 結果如下:
state            True
state/region    False
ages            False
year            False
population       True
dtype: bool   # 其中返回True的欄位中存在NAN空資料, 需要進行處理
# 怎麼計算丟失資料的數量
# state  population
abbrToPop['state'].isnull().sum()
# 結果如下:  96
# 把空資料填充上, 得到NAN行的篩選條件
cond = abbrToPop['state'].isnull()

# state是州名, 如何填充
# unique()去除重複的值
abbrToPop['state/region'][cond].unique()
# 結果如下:array(['PR', 'USA'], dtype=object)
# 我們通過翻閱資料查到了PR的全稱
# Puerto Rico
# 開始賦值
cond_pr = abbrToPop['state/region'] == 'PR'
abbrToPop['state'][cond_pr] = 'Puerto Rico'
cond_usa = abbrToPop['state/region'] == 'USA'
abbrToPop['state'][cond_usa] = 'United States'
abbrToPop.isnull().any()
# 結果如下:
state           False
state/region    False
ages            False
year            False
population       True
dtype: bool   # 成功將state資料修復
abbrToPop.dropna(inplace=True)
# population 查閱資料, 我們先刪除掉
abbrToPop.isnull().sum()
# 結果:
state           0
state/region    0
ages            0
year            0
population      0
dtype: int64
# 還有一個表需要合併
areas.head()
# 結果如下

這裡寫圖片描述

# 融合面積
abbrToPopToAreas = pd.merge(abbrToPop,areas, on='state',how='outer' )
abbrToPopToAreas.head()

這裡寫圖片描述

abbrToPopToAreas.isnull().sum()
# 結果如下:
state             0
state/region      0
ages              0
year              0
population        0
area (sq. mi)    48
dtype: int64   # area欄位有48個空值
cond_area = abbrToPopToAreas['area (sq. mi)'].isnull()
total_area = areas['area (sq. mi)'].sum()
total_area  # 結果是: 3790399
# U.S.A
cond_ab = abbrToPopToAreas['state/region'] == 'USA'

abbrToPopToAreas['area (sq. mi)'][cond_ab] = total_area
abbrToPopToAreas.isnull().sum()
# 資料處理完畢後結果:
state            0
state/region     0
ages             0
year             0
population       0
area (sq. mi)    0
dtype: int64
# 現在的表已經完全融合完成了
# 現在可以進行分析了

找出2010年全民人口資料, df.query()

abbrToPopToAreas_2010 = abbrToPopToAreas.query('year == 2010 & ages == "total"')
abbrToPopToAreas_2010.head()
# 結果如下:

這裡寫圖片描述

以state作為列索引

# 工作中會使用id作為列的索引
# set_index()
abbrToPopToAreas_2010.set_index('state', inplace=True)
abbrToPopToAreas_2010.head()

這裡寫圖片描述

計算人口的密度, population / area (sq. mi)

density_2010 = abbrToPopToAreas_2010['population'] / abbrToPopToAreas_2010['area (sq. mi)']
density_2010
# 結果如下:
state
Alabama                   91.287603
Alaska                     1.087509
Arizona                   56.214497
Arkansas                  54.948667
California               228.051342
Colorado                  48.493718
Connecticut              645.600649
Delaware                 460.445752
District of Columbia    8898.897059
Florida                  286.597129
Georgia                  163.409902
Hawaii                   124.746707
Idaho                     18.794338
Illinois                 221.687472
Indiana                  178.197831
Iowa                      54.202751
Kansas                    34.745266
Kentucky                 107.586994
Louisiana                 87.676099
Maine                     37.509990
Montana                    6.736171
Nebraska                  23.654153
Nevada                    24.448796
New Hampshire            140.799273
New Jersey              1009.253268
New Mexico                16.982737
New York                 356.094135
North Carolina           177.617157
North Dakota               9.537565
Ohio                     257.549634
Oklahoma                  53.778278
Oregon                    39.001565
Maryland                 466.445797
Massachusetts            621.815538
Michigan                 102.015794
Minnesota                 61.078373
Mississippi               61.321530
Missouri                  86.015622
Pennsylvania             275.966651
Rhode Island             681.339159
South Carolina           144.854594
South Dakota              10.583512
Tennessee                150.825298
Texas                     93.987655
Utah                      32.677188
Vermont                   65.085075
Virginia                 187.622273
Washington                94.557817
West Virginia             76.519582
Wisconsin                 86.851900
Wyoming                    5.768079
Puerto Rico             1058.665149
United States             81.607845
dtype: float64

2010年的人口密度融合到表中

abbrToPopToAreas_2010['density_2010'] = density_2010
abbrToPopToAreas_2010.head()

這裡寫圖片描述

排序,找出人口密度最高的五個州¶

# sort_values 根據值來進行排序

abbrToPopToAreas_2010.sort_values(by='density_2010').tail()

這裡寫圖片描述

蘋果股票漲跌圖繪製

import pandas as pd
from pandas import Series, DataFrame
import matplotlib.pyplot as plt
import numpy as np
apple = pd.read_csv('../data/AAPL.csv')
apple.head()
# 結果如下:

這裡寫圖片描述

apple.dtypes
# 結果如下:
Date          object
Open         float64
High         float64
Low          float64
Close        float64
Adj Close    float64
Volume       float64
dtype: object  # 在這裡可以看見Date是object型別的

轉換一下data的資料型別

mysql 中有datetime pd .to_datetime()

apple['Date'] = pd.to_datetime(apple['Date'])
apple.dtypes
# 結果如下:
Date         datetime64[ns]
Open                float64
High                float64
Low                 float64
Close               float64
Adj Close           float64
Volume              float64
dtype: object    # Date的資料型別轉換為datetime64
apple.set_index('Date', inplace=True)
# 結果如下:

這裡寫圖片描述

繪製圖形

adj_plot = apple['Adj Close'].plot()
fig = adj_plot.get_figure()

# set_size_inches 設定圖片的大小, 單位是英寸
fig.set_size_inches(12,6)
# 結果如下:

這裡寫圖片描述

# 因為Volume這一列資料量級太大,不適合分析,故刪除
apple.drop('Volume', axis=1, inplace=True)
app = apple.plot()
fig1 = app.get_figure()
fig1.set_size_inches(12,6)

這裡寫圖片描述