您當前的位置:首頁 > 農業

第 2 章 CSV 檔案

作者:由 wyqdgggfk 發表于 農業時間:2020-02-06

第 2 章 CSV 檔案

2。1 基礎 Python 與 pandas

2。1。1 讀寫 CSV 檔案(第 1 部分)

基礎 Python,不使用 CSV 模組

如果不使用 python 的 csv 模組,那麼如何讀寫 csv 檔案?可參考以下程式碼:

import

sys

input_file

=

sys

argv

1

#獲取輸入的檔案

output_file

=

sys

argv

2

#獲取輸出的檔案

with

open

input_file

‘r’

newline

=

‘’

as

filereader

with

open

output_file

‘w’

newline

=

‘’

as

filewriter

header

=

filereader

readline

()

header

=

header

strip

()

header_list

=

header

split

‘,’

print

header_list

filewriter

write

‘,’

join

map

str

header_list

))

+

\n

for

row

in

filereader

row

=

row

strip

()

row_list

=

row

split

‘,’

print

row_list

filewriter

write

‘,’

join

map

str

row_list

))

+

\n

對於以上程式碼中的

sys。argv[]

,可參考 覆手為雲的介紹,弄明白什麼是 argv[] 後,再參考書中 ${P}

{53}-{P}

{54}$ 的操作方法,Mac 上的具體實現步驟如下:

開啟終端;

cd 命令到本檔案所儲存的檔案路徑:

cd /Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料

在終端輸入此命令:

python3 1csv_read_with_simple_parsing_and_write。py supplier_data。csv 1output。csv

,其中

python3

指的是讓終端執行 python3 的命令,而

1csv_read_with_simple_parsing_and_write。py

就是要執行的 python 命令,也就是書中的程式碼,

supplier_data。csv

是向這個命令提交的第一個引數,也就是程式碼中指代的

sys。argv[1]

,同理可得

1output。csv

是向程式碼提交的第二個引數,即

sys。argv[2]

最後呈上終端執行時的 gif 圖:

利用 Pandas 也可以處理 CSV 檔案,具體參考如下程式碼:

import

sys

import

pandas

as

pd

input_file

=

sys

argv

1

output_file

=

sys

argv

2

data_frame

=

pd

read_csv

input_file

print

data_frame

data_frame

to_csv

output_file

index

=

False

在實際執行時,我的終端總是提示我找不到 pandas_,發現是無法正確找到 pandas 這個庫,所以我稍微修改了一下原始碼,如下:

import

sys

import

pandas

as

pd

input_file

=

‘/Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料/supplier_data。csv’

output_file

=

‘/Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料/1output。csv’

data_frame

=

pd

read_csv

input_file

print

data_frame

data_frame

to_csv

output_file

index

=

False

請注意,以上程式碼的 input_file 和 output_file 要置換為你自己電腦上相應檔案的路徑,否則無法執行。

再來看看上面的程式碼在 CodeRunner 中的執行結果:

2。1。2 基本字串分析是如何失敗的

對於 1csv_read_with_simple_parsing_and_write。py 中的程式碼,要考慮一種情況,就是如果資料中有逗號怎麼辦,程式碼是以逗號分隔每行資料中的每個資料,如果資料本身有逗號,就會形成干擾。

2。1。3 讀寫 CSV 檔案(第 2 部分)

基礎 Python,使用 CSV 模組

使用 CSV 模組的一個好處就是:不需要僅僅為了正確處理資料而花費時間來設計正則表示式和條件邏輯。

將 supplier_data。csv 中 cost 一列的最下方兩個資料更改為 6,015。00 和 1,006,015。00,然後新建一個 。py 檔案,命名為 2csv_reader_parsing_and_write。py,儲存在對應的資料夾下,程式碼如下:

import

csv

import

sys

input_file

=

sys

argv

1

output_file

=

sys

argv

2

with

open

input_file

‘r’

newline

=

‘’

as

csv_in_file

with

open

output_file

‘w’

newline

=

‘’

as

csv_out_file

filereader

=

csv

reader

csv_in_file

delimiter

=

‘,’

filewriter

=

csv

writer

csv_out_file

delimiter

=

‘,’

for

row_list

in

filereader

print

row_list

filewriter

writerow

row_list

執行結果如下:

2。2 篩選特定的行

注意以下虛擬碼的結構

for

row

in

filereader

***

if

value

in

row

meets

some

business

rule

or

set

of

rules

***

do

something

else

do

something

else

2。2。1 行中的值滿足某個條件

基礎 Python

以下程式碼可檢驗行值是否滿足兩個具體條件,並將滿足條件的行的子集寫入一個輸出檔案:

import

csv

import

sys

input_file

=

sys

argv

1

output_file

=

sys

argv

2

with

open

input_file

‘r’

newline

=

‘’

as

csv_in_file

with

open

output_file

‘w’

newline

=

‘’

as

csv_out_file

filereader

=

csv

reader

csv_in_file

filewriter

=

csv

writer

csv_out_file

header

=

next

filereader

#使用 csv 模組的 next 函式讀出輸入檔案的第一行,賦名 header 列表

filewriter

writerow

header

for

row_list

in

filereader

#按行讀取資料

supplier

=

str

row_list

0

])

strip

()

cost

=

str

row_list

3

])

strip

‘$’

replace

‘,’

‘’

if

supplier

==

‘Supplier Z’

or

float

cost

>

600。0

filewriter

writerow

row_list

有了上述的程式碼,我們可以稍微修改一下,在 Kaggle 官網上找到 YouTube 的一些影片觀看資料來進行簡單的篩選,我已經將要讀取的名為 YouTubeReadFile。csv 的檔案放在倉庫中,具體程式碼如下:

import

csv

import

sys

input_file

=

sys

argv

1

#要讀取的 csv 檔名為 YouTubeReadFile。csv

output_file

=

sys

argv

2

#要寫入的 csv 檔名為 YouTubeWriteFile。csv

with

open

input_file

‘r’

newline

=

‘’

as

csv_in_file

with

open

output_file

‘w’

newline

=

‘’

as

csv_out_file

filereader

=

csv

reader

csv_in_file

filewriter

=

csv

writer

csv_out_file

header

=

next

filereader

filewriter

writerow

header

for

row_list

in

filereader

views

=

int

str

row_list

7

])

strip

())

# 觀看人數

likes

=

int

str

row_list

8

])

strip

())

# 點贊人數

if

views

>=

1147000

and

likes

>=

39000

# 篩選觀看人數和點贊人數均大於平均數的資料,共 5994 個

filewriter

writerow

row_list

利用 pandas 選擇符合特定條件值的行

loc 函式:pandas 提供的可以同時選擇特定行與列的函式。在逗號前面設定行篩選條件,在逗號後面設定列篩選條件。

如果我想在 supplier_data。csv 中篩選供應商名稱包含字母‘Z’,或者 cost 大於 600。0 的資料應該如何做呢?且看具體示例程式碼 pandas_value_meets_condition。py:

import

pandas

as

pd

import

sys

input_file

=

sys

argv

1

output_file

=

sys

argv

2

data_frame

=

pd

read_csv

input_file

#讀取輸入的表格

data_frame

‘Cost’

=

data_frame

‘Cost’

str

strip

‘$’

# 試試看,如果某個 Cost 的值有逗號會怎樣,比如 6,015。00

data_frame

‘Cost’

=

data_frame

‘Cost’

str

replace

‘,’

‘’

astype

float

data_frame_value_meets_condition

=

data_frame

loc

[(

data_frame

‘Supplier Name’

str

contains

‘Z’

))

|

data_frame

‘Cost’

>

600。0

),:]

data_frame_value_meets_condition

to_csv

output_file

index

=

False

原書的程式碼與本程式碼有些不一樣,原書沒有考慮到 Cost 數值中有逗號的情形,這在轉換為 float 時會報錯。

同樣參考以上程式碼,如果我想篩選出 YouTubeFile。csv 中 views 大於 114700,likes 大於 39000 且 comment_count 大於 5043 的電視節目,應該怎樣做呢?可以參考下面的這段程式碼:

import

sys

import

pandas

as

pd

input_file

=

sys

argv

1

output_file

=

sys

argv

2

data_frame

=

pd

read_csv

input_file

#讀取輸入的 csv 檔案,此處為 YouTubeReadFile。csv

data_frame

‘views’

=

data_frame

‘views’

astype

float

data_frame

‘likes’

=

data_frame

‘likes’

astype

float

data_frame

‘comment_count’

=

data_frame

‘comment_count’

astype

float

data_frame_value_meets_condition

=

data_frame

loc

[(

data_frame

‘views’

>=

1147000

&

data_frame

‘likes’

>=

39000

&

data_frame

‘comment_count’

>=

5043

),:]

data_frame_value_meets_condition

to_csv

output_file

index

=

False

這裡會將 YouTubeReadFile。csv 檔案中符合條件的值篩選出來,並存儲到 YouTube_pandas_value_meets_condition。py 中。

2。2。2 行中的值屬於某個集合

如果行中的某個值屬於某個範圍,也可以篩選出來,比如特定的某幾個日期,再比如特定的某幾種屬性,可參考以下程式碼:

import

csv

import

sys

input_file

=

sys

argv

1

#此處獲取的是 supplier_data。csv

output_file

=

sys

argv

2

#此處輸出的是 4output。csv

important_dates

=

‘1/20/14’

‘1/30/14’

with

open

input_file

‘r’

newline

=

‘’

as

csv_in_file

with

open

output_file

‘w’

newline

=

‘’

as

csv_out_file

filereader

=

csv

reader

csv_in_file

filewriter

=

csv

writer

csv_out_file

header

=

next

filereader

filewriter

writerow

header

for

row_list

in

filereader

a_date

=

row_list

4

if

a_date

in

important_dates

filewriter

writerow

row_list

以上程式碼是透過 csv 庫實現的效果,如果用 pandas 會更加簡單,如下所示:

import

pandas

as

pd

import

sys

input_file

=

sys

argv

1

output_file

=

sys

argv

2

data_frame

=

pd

read_csv

input_file

important_dates

=

‘1/20/14’

‘1/30/14’

data_frame_value_in_set

=

data_frame

loc

data_frame

‘Purchase Date’

isin

important_dates

),:]

#isin 這個命令很簡單實用

data_frame_value_in_set

to_csv

output_file

index

=

False

2。2。3 行中的值匹配於某個模式/正則表示式

正則表示式一般用於查詢某種通用規律的資料,例如:

身份證號開頭是 110102 的人,這代表某人的籍貫是北京;

學號第二位到第五位是 2018 的學生,通常這可能意味著他是 20 年入學,而 18 代表學院編號;

在某個城市,車牌尾號的數字是 1,3,5 的汽車在週一,週三,週五限行。

同樣的例子還有很多,可以透過給出的資料進行篩選,下面看一段書中用 csv 和 re 兩個庫實現的程式碼:

import

csv

import

re

import

sys

input_file

=

sys

argv

1

#此處為 supplier_data。csv

output_file

=

sys

argv

2

]]

#此處為 5csv_reader_value_matches_pattern。csv

pattern

=

re

compile

r

‘(?P^001-。*)’

re

I

with

open

input_file

‘r’

newline

=

‘’

as

csv_in_file

with

open

output_file

‘w’

newline

=

‘’

as

csv_out_file

filereader

=

csv

reader

csv_in_file

filewriter

=

csv

writer

csv_out_file

header

=

next

filereader

filewriter

writerow

header

for

row_list

in

filereader

invoice_number

=

row_list

1

if

pattern

search

invoice_number

):

filewriter

writerow

row_list

如何執行?此處只是補充說明,實際上在之前的筆記中有過講解,只要在終端用 cd 命令先導航到此 。py 檔案的路徑下,比如在我的 Mac 上就是

cd /Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料

然後執行此命令

python3 5csv_reader_value_matches_pattern。py supplier_data。csv 5csv_reader_value_matches_pattern。csv

即可將 supplier_data。csv 中符合要求的資料寫入 5csv_reader_value_matches_pattern。csv 中。

在以上程式碼中,我們要搜尋的是以 “001-”開頭的的字串(注意 001 後面的那一根短橫線,也是要匹配的物件),實心句號代表匹配除了換行符的任意字元,而星號則是匹配多個前面的字元,那麼 “*” 連起來的意思就是“匹配除換行符以外的多個字元”,re。I 的意思是進行大小寫敏感的匹配,當然在這段程式碼中並不重要。

同樣的功能,如果用 pandas 來實現,程式碼量會更少,可參考如下:

import

pandas

as

pd

import

sys

input_file

=

sys

argv

1

#此處的檔案還是 supplier_data。csv

output_file

=

sys

argv

2

#此處的檔案是 pandas_value_matches_pattern_5output。csv

data_frame

=

pd

read_csv

input_file

data_frame_value_matches_pattern

=

data_frame

loc

data_frame

‘Invoice Number’

str

startswith

“001-”

),:]

data_frame_value_matches_pattern

to_csv

output_file

index

=

False

需要注意的是,我在用 CodeRunner 寫 pandas 時,總是不能自動補全,導致一些細節錯誤無法正常執行程式碼,不過,這也正好可以練習一下寫程式碼的基本功,畢竟一些常用的功能模組是需要記住的,不能都靠程式碼補全功能。

2。3 選取特定的列

2。3。1 列索引值

在 CSV 檔案中選取特定的列的一種辦法就是使用對應列的索引值,當然,這有一些限定條件,比如:

想保留的列的索引值非常容易識別到;

處理多個輸入檔案時,各個輸入檔案中列的位置一致。

對於 supplier_data。csv 檔案中,如果我們想只保留供應商名稱和成本這兩列,我們就可以使用索引值來選取這兩列,書中將以下程式碼儲存為 6csv_reader_column_by_index。py:

import

csv

import

sys

input_file

=

sys

argv

1

#此處為 supplier_data。csv

output_file

=

sys

argv

2

#此處為 6output。csv

my_columns

=

0

3

with

open

input_file

‘r’

newline

=

‘’

as

csv_in_file

with

open

output_file

‘w’

newline

=

‘’

as

csv_out_file

filereader

=

csv

reader

csv_in_file

filewriter

=

csv

writer

csv_out_file

for

row_list

in

filereader

row_list_output

=

[]

for

index_value

in

my_columns

row_list_output

append

row_list

index_value

])

filewriter

writerow

row_list_output

完成以上程式碼後,在資料資料夾中建立一個空白的 6output。csv 檔案,然後終端執行如下命令:

先導航到 。py 檔案所在路徑下

cd /Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料

執行此命令:

python3 6csv_reader_column_by_index。py supplier_data。csv 6output。csv

終端不會有任何輸出,但是此時開啟 6output。csv 檔案,就能看到供應商名稱和對應的價格了。

同樣的功能,我們來稍微做個小練習,找到一個名為 DEvideos。csv 的檔案,此檔案是從 Kaggle 網站下載的 YouTube 觀看資料之一,開啟檢查一下這個 csv 檔案,第一排有 video_id,trending_date,title,channel_title,category_id 等等資訊,現在的任務是,要抓取比較多的資料,具體要求如下:

抓取的資料應該包括除了 description 以外的所有資料;

對於 publish_time,僅保留日期,不用保留具體時間;

每抓取一條資訊,都在終端列印下來。

為了處理這個問題,我們依次來看要求,首先是抓取的資料要除開 description,這個比較好辦,一共有 16 列的資料,而且 description 剛好在最後,只要一個 range 函式就可以了;其次是 publish_time,僅保留日期,不需要時間,這個稍微麻煩一點點,需要用到正則匹配;最後是列印下來,這個沒什麼難度了,就是直接列印,只是在實際列印的過程中,我發現如果不列印,那麼程度處理得會很快,如果列印,CodeRunner 出現了轉綵球的狀況,為了演示,我們加入一個簡單的延時 time。sleep(0。001),在實際操作時可以根據自己的情況酌情考慮是否延時,請看下面的程式碼:

import

csv

import

re

import

time

pattern

=

re

compile

‘T[\d]+:[\d]+:[\S]+’

# 正則表示式,用來篩選 publish_time 中的日期

“”“實在不想在寫好程式碼後,跑去終端執行,就直接把路徑寫下來吧”“”

input_file

=

‘/Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料/Trending_YouTube_Video_Statistics/DEvideos。csv’

output_file

=

‘/Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料/DEvideo_write_column_by_index。csv’

with

open

input_file

‘r’

newline

=

‘’

as

csv_in_file

with

open

output_file

‘w’

newline

=

‘’

as

csv_out_file

filereader

=

csv

reader

csv_in_file

filewriter

=

csv

writer

csv_out_file

for

row_list

in

filereader

row_list_output

=

[]

for

i

in

range

15

):

if

i

==

5

row_list

i

=

re

sub

pattern

‘’

row_list

i

])

#將 publish_time 中日期後面的部分用替換的方式刪除掉

row_list_output

append

row_list

i

])

filewriter

writerow

row_list_output

print

row_list_output

time

sleep

0。001

書中還給出了利用 pandas 挑選指定列的方法,比使用 csv 會更加簡單,如下所示:

import

pandas

as

pd

import

sys

input_file

=

sys

argv

1

output_file

=

sys

argv

2

data_frame

=

pd

read_csv

input_file

data_frame_column_by_index

=

data_frame

iloc

[:,[

0

3

]]

data_frame_column_by_index

to_csv

output_file

index

=

False

很明顯,用 pandas 會比用 csv 更加簡單,程式碼量也更少。

2。3。2 列標題

除了用索引選取特定列以外,還可以在 csv 檔案中使用列標題來選取特定的列,可參考如下程式碼:

import

csv

import

sys

input_file

=

sys

argv

1

output_file

=

sys

argv

2

my_columns

=

‘Invoice Number’

‘Purchase Date’

my_columns_index

=

[]

with

open

input_file

‘r’

newline

=

‘’

as

csv_in_file

with

open

output_file

‘w’

newline

=

‘’

as

csv_out_file

filereader

=

csv

reader

csv_in_file

filewriter

=

csv

writer

csv_out_file

header

=

next

filereader

None

for

index_value

in

range

len

header

)):

if

header

index_value

in

my_columns

my_columns_index

append

index_value

filewriter

writerow

my_columns

for

row_list

in

filereader

row_list_output

=

[]

for

index_value

in

my_columns_index

row_list_output

append

row_list

index_value

])

filewriter

writerow

row_list_output

原書的程式碼有一處錯誤,倒數第二排的 row_list_output。append(row_list[index_value]) 沒有縮排。另外,最後一排程式碼,filewriter。writerow(row_list_output),我不清楚是我機器的問題還是書中程式碼的問題,這一段程式碼也需要縮排到第二個 for 迴圈下,而不是第一個 for 迴圈下,如果不縮排,那麼在我的 Mac 上執行時,只讀取到了 supplier_data。csv 中最後一排的發票和價格。

2。4 選取連續的行

書中提到有時我們可能會遇到工作表的頭部和尾部都是不想處理的資訊,此時需要選擇那些我們需要處理的資料,可參考以下程式碼:

import

csv

import

sys

input_file

=

sys

argv

1

# 此處是 supplier_data_unnecessary_header_footer。csv

output_file

=

sys

argv

2

# 此處是 11output。csv

row_counter

=

0

with

open

input_file

‘r’

newline

=

‘’

as

csv_in_file

with

open

output_file

‘w’

newline

=

‘’

as

csv_out_file

filereader

=

csv

reader

csv_in_file

filewriter

=

csv

writer

csv_out_file

for

row

in

filereader

if

row_counter

>=

3

and

row_counter

<=

15

filewriter

writerow

([

value

strip

()

for

value

in

row

])

row_counter

+=

1

以上程式碼可以在 supplier_data_unnecessary_header_footer。csv 這個檔案中跳過行開頭的資料,過濾行結尾的資料,只選擇我們需要的部分。

另外,在實際操作過程中,我將 supplier_data。csv 另存為一個新的 csv 檔案,新增三行 “I don‘t care this line” 到表頭,又新增三行“I don’t care this line either” 到表尾,並儲存成 supplier_data_unnecessary_header_footer。csv 時,執行上述程式碼遇到一個問題,錯誤提示是“UnicodeDecodeError: ‘utf-8’ codec can‘t decode byte 0xd5 in position 5: invalid continuation byte”,網上查了一下這應該是 utf-8 的解碼問題,如果您也遇到了類似問題,不妨試試我的方法:把所有單元格的內容整體複製下來,新建一個 csv 檔案貼上進去,我是這樣解決的。

剛剛是用 Python 自帶的 csv 庫完成了篩選特定行的操作,如果用 Pandas 的話,其實會更加簡單,如下所示:

import

pandas

as

pd

import

sys

input_file

=

sys

argv

1

# 此處為 supplier_data_unnecessary_header_footer。csv

output_file

=

sys

argv

2

# 此處為 pandas_output_select_contiguous_rows。csv

data_frame

=

pd

read_csv

input_file

header

=

None

data_frame

=

data_frame

drop

([

0

1

2

16

17

18

])

data_frame

columns

=

data_frame

iloc

0

indexInfo

=

data_frame

index

drop

3

data_frame

=

data_frame

reindex

data_frame

index

drop

3

))

# 為了弄懂這一行程式碼的含義,我在 csv 檔案中加了一列 indextest 索引,從 1 到 12

data_frame

to_csv

output_file

index

=

False

當然,一開始我是沒有弄懂這段程式碼的,比如這一句

data_frame

=

data_frame

reindex

data_frame

index

drop

3

))

到底能達到什麼目的?在網上查了一下,drop() 函式的功能是把 data_frame 中對應的行或列的值拋掉,那麼

data_frame。index。drop(3)

是什麼鬼,開啟已經操作好的 pandas_output_select_contiguous_rows。csv 檔案,它是長這個樣子的:

| Supplier Name | Invoice Number | Part Number | Cost | Purchase Date | indextest | | ——————- | ———————— | ——————- | —————— | ——————- | ————- | | Supplier X | 001-1001 | 2341 | $500。00 | 1/20/14 | 1 | | Supplier X | 001-1001 | 2341 | $500。00 | 1/20/14 | 2 | | Supplier X | 001-1001 | 5467 | $750。00 | 1/20/14 | 3 | | Supplier X | 001-1001 | 5467 | $750。00 | 1/20/14 | 4 | | Supplier Y | 50-9501 | 7009 | $250。00 | 1/30/14 | 5 | | Supplier Y | 50-9501 | 7009 | $250。00 | 1/30/14 | 6 | | Supplier Y | 50-9505 | 6650 | $125。00 | 2002/3/14 | 7 | | Supplier Y | 50-9505 | 6650 | $125。00 | 2002/3/14 | 8 | | Supplier Z | 920-4803 | 3321 | $615。00 | 2002/3/14 | 9 | | Supplier Z | 920-4803 | 3321 | $615。00 | 2002/10/14 | 10 | | Supplier Z | 920-4803 | 3321 | $60,15。00 | 2/17/14 | 11 | | Supplier Z | 920-4803 | 3321 | $10,06015。00 | 2/24/14 | 12 |

換句話講,書中的 pandas 程式碼的確達到了我們想要的結果,即篩選特定行的資料,我們試試看逐個測試這段程式碼,並將結果列印下來,看看是怎樣。

首先把

input_file

output_file

都直接表示為路徑,即

input_file

=

’/Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料/supplier_data_unnecessary_header_footer。csv‘

# 請注意此處需要替換為您自己電腦上對應檔案的路徑

output_file

=

’/Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料/pandas_output_select_contiguous_rows。csv‘

# 請注意此處需要替換為您自己電腦上對應檔案的路徑

這一步操作是為了可以直接在 CodeRunner 中執行出結果,省掉了終端命令的過程。

接下來,分別在每一行程式碼的下方加一句

print()

,檢視當前狀態下的各種資訊是怎樣的:

# 第一次 print

import

pandas

as

pd

import

sys

input_file

=

’/Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料/supplier_data_unnecessary_header_footer。csv‘

# 請注意此處需要替換為您自己電腦上對應檔案的路徑

output_file

=

’/Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料/pandas_output_select_contiguous_rows。csv‘

# 請注意此處需要替換為您自己電腦上對應檔案的路徑

data_frame

=

pd

read_csv

input_file

header

=

None

print

data_frame

此時的輸出是長這樣:

| | 0 | 1 | 。。。 | 4 | 5 | | —— | ——————————————- | ———————— | —— | ——————- | ————- | | 0 | I don’t care this line | NaN | 。。。 | NaN | NaN | | 1 | I don‘t care this line | NaN | 。。。 | NaN | NaN | | 2 | I don’t care this line | NaN | 。。。 | NaN | NaN | | 3 | Supplier Name | Invoice Number | 。。。 | Purchase Date | indextest | | 4 | Supplier X | 001-1001 | 。。。 | 1/20/14 | 1 | | 5 | Supplier X | 001-1001 | 。。。 | 1/20/14 | 2 | | 6 | Supplier X | 001-1001 | 。。。 | 1/20/14 | 3 | | 7 | Supplier X | 001-1001 | 。。。 | 1/20/14 | 4 | | 8 | Supplier Y | 50-9501 | 。。。 | 1/30/14 | 5 | | 9 | Supplier Y | 50-9501 | 。。。 | 1/30/14 | 6 | | 10 | Supplier Y | 50-9505 | 。。。 | 2002/3/14 | 7 | | 11 | Supplier Y | 50-9505 | 。。。 | 2002/3/14 | 8 | | 12 | Supplier Z | 920-4803 | 。。。 | 2002/3/14 | 9 | | 13 | Supplier Z | 920-4804 | 。。。 | 2002/10/14 | 10 | | 14 | Supplier Z | 920-4805 | 。。。 | 2/17/14 | 11 | | 15 | Supplier Z | 920-4806 | 。。。 | 2/24/14 | 12 | | 16 | I don‘t care this line either | NaN | 。。。 | NaN | NaN | | 17 | I don’t care this line either | NaN | 。。。 | NaN | NaN | | 18 | I don‘t care this line either | NaN | 。。。 | NaN | NaN |

利用

data_frame = pd。read_csv(input_file,header=None)

這一句程式碼,將 supplier_data_unnecessary_header_footer。csv 檔案中的所有資訊賦值給了 data_frame,不得不說真心方便。

# 第二次 print

import

pandas

as

pd

import

sys

input_file

=

’/Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料/supplier_data_unnecessary_header_footer。csv‘

# 請注意此處需要替換為您自己電腦上對應檔案的路徑

output_file

=

’/Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料/pandas_output_select_contiguous_rows。csv‘

# 請注意此處需要替換為您自己電腦上對應檔案的路徑

data_frame

=

pd

read_csv

input_file

header

=

None

data_frame

=

data_frame

drop

([

0

1

2

16

17

18

])

print

data_frame

此時的輸出已經沒有了開頭的

I don’t care this line

和結尾的

I don‘t care this line either

,其輸出為:

| | 0 | 1 | 2 | 3 | 4 | 5 | | —— | ——————- | ———————— | ——————- | —————— | ——————- | ————- | | 3 | Supplier Name | Invoice Number | Part Number | Cost | Purchase Date | indextest | | 4 | Supplier X | 001-1001 | 2341 | $500。00 | 1/20/14 | 1 | | 5 | Supplier X | 001-1001 | 2341 | $500。00 | 1/20/14 | 2 | | 6 | Supplier X | 001-1001 | 5467 | $750。00 | 1/20/14 | 3 | | 7 | Supplier X | 001-1001 | 5467 | $750。00 | 1/20/14 | 4 | | 8 | Supplier Y | 50-9501 | 7009 | $250。00 | 1/30/14 | 5 | | 9 | Supplier Y | 50-9501 | 7009 | $250。00 | 1/30/14 | 6 | | 10 | Supplier Y | 50-9505 | 6650 | $125。00 | 2002/3/14 | 7 | | 11 | Supplier Y | 50-9505 | 6650 | $125。00 | 2002/3/14 | 8 | | 12 | Supplier Z | 920-4803 | 3321 | $615。00 | 2002/3/14 | 9 | | 13 | Supplier Z | 920-4804 | 3321 | $615。00 | 2002/10/14 | 10 | | 14 | Supplier Z | 920-4805 | 3321 | $60,15。00 | 2/17/14 | 11 | | 15 | Supplier Z | 920-4806 | 3321 | $10,06015。00 | 2/24/14 | 12 |

上面的程式碼用了一段 drop 函式

data_frame = data_frame。drop([0,1,2,16,17,18])

將第 0,1,2,16,17,18 行資料刪除掉。

然後我們再來看看

data_frame。iloc[0]

能幹什麼,書上是說可以使用 iloc 這個函式根據行索引選取一個單獨行作為列索引,那麼使用 iloc[0] 應該就是把第 0 行的各個單元格的值作為索引,從實際程式碼來看,也確實如此:

# 第三次 print

import

pandas

as

pd

import

sys

input_file

=

’/Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料/supplier_data_unnecessary_header_footer。csv‘

# 請注意此處需要替換為您自己電腦上對應檔案的路徑

output_file

=

’/Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料/pandas_output_select_contiguous_rows。csv‘

# 請注意此處需要替換為您自己電腦上對應檔案的路徑

data_frame

=

pd

read_csv

input_file

header

=

None

data_frame

=

data_frame

drop

([

0

1

2

16

17

18

])

data_frame

columns

=

data_frame

iloc

0

print

data_frame

這一次

print

把上一次列印的第一排的 0 1 2 3 4 5 換成了列標題,如下所示:

| 3 | Supplier Name | Invoice Number | Part Number | Cost | Purchase Date | indextest | | —— | ——————- | ———————— | ——————- | —————— | ——————- | ————- | | 3 | Supplier Name | Invoice Number | Part Number | Cost | Purchase Date | indextest | | 4 | Supplier X | 001-1001 | 2341 | $500。00 | 1/20/14 | 1 | | 5 | Supplier X | 001-1001 | 2341 | $500。00 | 1/20/14 | 2 | | 6 | Supplier X | 001-1001 | 5467 | $750。00 | 1/20/14 | 3 | | 7 | Supplier X | 001-1001 | 5467 | $750。00 | 1/20/14 | 4 | | 8 | Supplier Y | 50-9501 | 7009 | $250。00 | 1/30/14 | 5 | | 9 | Supplier Y | 50-9501 | 7009 | $250。00 | 1/30/14 | 6 | | 10 | Supplier Y | 50-9505 | 6650 | $125。00 | 2002/3/14 | 7 | | 11 | Supplier Y | 50-9505 | 6650 | $125。00 | 2002/3/14 | 8 | | 12 | Supplier Z | 920-4803 | 3321 | $615。00 | 2002/3/14 | 9 | | 13 | Supplier Z | 920-4804 | 3321 | $615。00 | 2002/10/14 | 10 | | 14 | Supplier Z | 920-4805 | 3321 | $60,15。00 | 2/17/14 | 11 | | 15 | Supplier Z | 920-4806 | 3321 | $10,06015。00 | 2/24/14 | 12 |

我試過把程式碼中的

iloc[0]

換成

iloc[1]

,此時上面的列標題也會隨之更換,意味著我們透過

iloc[]

這個函式實現了重新根據行索引選取一個單獨行來作為列索引,可是明明在

data_frame = data_frame。drop([0,1,2,16,17,18])

中不是已經把第 0 行丟掉了麼?是的,丟掉了,

iloc[0]

在這段程式碼裡面指的也不是最初的第 0 行,請看錶格最左的數字,

iloc[0]

指代的是數字 3 那一行。

再做一個小測試,在上一次列印時我們可以看到索引為 3 的那一行重複了,那麼如果在

data_frame。columns = data_frame。iloc[0]

的下面加一行,把表格重複的內容去掉,應該如何操作?我試過

data_frame = data_frame。drop(3)

是可行的,下面是具體程式碼和列印下來的表格:

# 第四次 print

import

pandas

as

pd

import

sys

input_file

=

’/Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料/supplier_data_unnecessary_header_footer。csv‘

# 請注意此處需要替換為您自己電腦上對應檔案的路徑

output_file

=

’/Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料/pandas_output_select_contiguous_rows。csv‘

# 請注意此處需要替換為您自己電腦上對應檔案的路徑

data_frame

=

pd

read_csv

input_file

header

=

None

data_frame

=

data_frame

drop

([

0

1

2

16

17

18

])

data_frame

columns

=

data_frame

iloc

0

data_frame

=

data_frame

drop

3

print

data_frame

| 3 | Supplier Name | Invoice Number | Part Number | Cost | Purchase Date | indextest | | —— | ——————- | ———————— | ——————- | —————— | ——————- | ————- | | 4 | Supplier X | 001-1001 | 2341 | $500。00 | 1/20/14 | 1 | | 5 | Supplier X | 001-1001 | 2341 | $500。00 | 1/20/14 | 2 | | 6 | Supplier X | 001-1001 | 5467 | $750。00 | 1/20/14 | 3 | | 7 | Supplier X | 001-1001 | 5467 | $750。00 | 1/20/14 | 4 | | 8 | Supplier Y | 50-9501 | 7009 | $250。00 | 1/30/14 | 5 | | 9 | Supplier Y | 50-9501 | 7009 | $250。00 | 1/30/14 | 6 | | 10 | Supplier Y | 50-9505 | 6650 | $125。00 | 2002/3/14 | 7 | | 11 | Supplier Y | 50-9505 | 6650 | $125。00 | 2002/3/14 | 8 | | 12 | Supplier Z | 920-4803 | 3321 | $615。00 | 2002/3/14 | 9 | | 13 | Supplier Z | 920-4804 | 3321 | $615。00 | 2002/10/14 | 10 | | 14 | Supplier Z | 920-4805 | 3321 | $60,15。00 | 2/17/14 | 11 | | 15 | Supplier Z | 920-4806 | 3321 | $10,06015。00 | 2/24/14 | 12 |

這證明

data_frame。drop(3)

這段命令的確把多餘的 Supplier Name 那一行去掉了,注意去掉的是第二個 Supplier Name 那一行,並不是第一個。

好了,測試完成,讓我們回到書中程式碼本身,並再來一個列印,看看究竟有什麼變化:

# 第五次 print

import

pandas

as

pd

import

sys

input_file

=

’/Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料/supplier_data_unnecessary_header_footer。csv‘

# 請注意此處需要替換為您自己電腦上對應檔案的路徑

output_file

=

’/Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料/pandas_output_select_contiguous_rows。csv‘

# 請注意此處需要替換為您自己電腦上對應檔案的路徑

data_frame

=

pd

read_csv

input_file

header

=

None

data_frame

=

data_frame

drop

([

0

1

2

16

17

18

])

data_frame

columns

=

data_frame

iloc

0

data_frame

=

data_frame

reindex

data_frame

index

drop

3

))

# 為了弄懂這一行程式碼的含義,我在 csv 檔案中加了一列 indextest 索引,從 1 到 12

print

data_frame

第五次

print

中,主要就是解釋

data_frame = data_frame。reindex(data_frame。index。drop(3))

這一段的含義,按照剛剛小測試的結果,

data_frame。index。drop(3)

丟掉了第二個 Supplier Name 那一行,而

data_frame。reindex()

的含義就是重新建立索引,合起來就是,先刪掉多餘的 Supplier Name,再重建索引,其列印結果與上面的那個表格一致,最後再寫入到新的 csv 檔案即可。

2。5 新增標題行

這一節的意思是,有時我們會收到沒有標題行的 csv 資料,需要自己新增標題行,其實有時候也會遇到需要修改標題行的情況,可參考以下程式碼新增標題行:

import

sys

import

csv

“”“

開始之前,請開啟 supplier_data。csv 檔案,把標題行刪了,然後將此檔案另存為supplier_data_no_header_row。csv

”“”

input_file

=

sys

argv

1

# 此處為 supplier_data_no_header_row。csv

output_file

=

sys

argv

2

# 此處為 12output。csv

with

open

input_file

’r‘

newline

=

’‘

as

csv_in_file

with

open

output_file

’w‘

newline

=

’‘

as

csv_out_file

filereader

=

csv

reader

csv_in_file

filewriter

=

csv

writer

csv_out_file

header_list

=

’Supplier Name‘

’Invoice Number‘

’Part Number‘

’Cost‘

’Purchase Date‘

filewriter

writerow

header_list

for

row

in

filereader

filewriter

writerow

row

同樣的功能,如果用 pandas 會更簡單一些,畢竟書中的原話是“pandas 中的 read_csv 可以直接指定輸入檔案不包含標題行,並可以提供一個列標題列表”,可參考以下程式碼:

import

pandas

as

pd

import

sys

input_file

=

sys

argv

1

# 此處為 supplier_data_no_header_row。csv

output_file

=

sys

argv

2

# 此處為 pandas_add_header_row_output。csv

header_list

=

’Supplier Name‘

’Invoice Number‘

’Part Number‘

’Cost‘

’Purchase Date‘

data_frame

=

pd

read_csv

input_file

header

=

None

names

=

header_list

data_frame

to_csv

output_file

index

=

False

2。6 讀取多個 CSV 檔案

書中需要讓讀者自行建立三個 csv 檔案,分別是 sales_january_2014。csv,sales_february_2014。csv,sales_march_2014。csv,所建立的 csv 檔案內容是 Customer ID,Customer Name,Invoice Number,Sale Amount,Purchase Date 這種資訊,那麼我們不妨結合一下之前所學過的內容,試試看統計一下我自己從國家統計局獲取的房地產開發投資情況,這裡面我已經準備好了 excel 檔案和 csv 檔案,稍後我們會先從 csv 檔案入手。

檔案計數與檔案中的行列計數

開始之前先看看兩個要用到的東西,一個是 glob 庫,一個是 os。path。join() 函式。

首先看一個 Python 自帶的庫 glob,參考了一下網上的資料,這個庫的功能就是獲取當前資料夾下的子檔案和子資料夾,用 * 作萬用字元匹配,比如下面的程式碼:

import

glob

testpath

=

’/*‘

#獲取根目錄下的所有資料夾

for

name

in

glob

glob

testpath

):

print

name

這段程式碼會獲取我電腦上根目錄資料夾的所有子資料夾,並將其列印下來。再比如下面的程式碼會獲取我電腦上所有安裝的軟體名稱,不過是以路徑形式:

import

glob

testpath

=

’/Applications/*‘

#獲取應用程式目錄下的所有軟體

for

name

in

glob

glob

testpath

):

print

name

接著看看 os。path。join() 函式的功能,實際上它和字串的拼接有點像,但它主要是針對路徑的,如果路徑中沒有 \,它可以自動補全,舉個例子:

import

os

path1

=

’home‘

path2

=

’admin‘

path3

=

’document‘

path_final

=

os

path

join

path1

path2

path3

print

path_final

# 此時輸出的 path_final 就是 home/admin/document

明白了這些之後,如果我們想要看懂書上的 8csv_reader_counts_for_multiple_files。py 原始碼,還有一個需要了解,試想一下,如果我們要找某個資料夾中所有以 pandas 開頭的檔案,應該怎樣處理?我們試試看找一下之前我們儲存過的以 pandas 開頭的檔案:

import

os

import

glob

test_file

=

’/Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料‘

# 注意此處要換成您自己電腦上對應的路徑

for

pandasfile

in

glob

glob

os

path

join

test_file

’pandas_*‘

)):

print

pandasfile

這裡用到了

glob。glob(os。path。join(test_file, ’pandas_*‘))

,實際上這就是我們要找的當前資料夾下所有以 pandas_ 開頭的檔案,用 * 表示通配查詢,列印下來的結果如下:

“”“

/Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料/pandas_output_select_contiguous_rows。csv

/Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料/pandas_select_contiguous_rows。py

/Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料/pandas_value_in_set。py

/Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料/pandas_value_meets_condition。py

/Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料/pandas_add_header_row_output。csv

/Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料/pandas_value_meets_condition。csv

/Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料/pandas_value_matches_pattern。py

/Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料/pandas_column_by_index。py

/Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料/pandas_parsing_and_write。gif

/Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料/pandas_add_header_row。py

/Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料/pandas_value_matches_pattern_5output。csv

/Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料/pandas_output_column_by_index。csv

/Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料/pandas_output。csv

/Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/第2章所需資料/pandas_ parsing_and_write。py

”“”

好了,我們可以開始學習書中第 73 頁的程式碼了,原始碼如下:

import

csv

import

glob

import

os

import

sys

input_path

=

sys

argv

1

file_counter

=

0

for

input_file

in

glob

glob

os

path

join

input_path

’sales_*‘

)):

row_counter

=

1

with

open

input_file

’r‘

newline

=

’‘

as

csv_in_file

filereader

=

csv

reader

csv_in_file

header

=

next

filereader

None

for

row

in

filereader

row_counter

+=

1

print

{0!s}

\t

{1:d}

rows

\t

{2:d}

columns‘

format

os

path

basename

input_file

),

row_counter

len

header

)))

file_counter

+=

1

print

’Number of files:

{0:d}

format

file_counter

))

原書中說執行上面的程式碼需要在終端執行下面這段程式碼:

python 8csv_reader_counts_for_multiple_files。py “C:\Users\Clinton\Desktop”

由於我並沒有去建立 sales 檔案,且自己找到了一些資料,所以我將上面的程式碼稍微進行了一點修改,請看如下:

import

csv

import

glob

import

os

import

sys

input_path

=

sys

argv

1

file_counter

=

0

for

input_file

in

glob

glob

os

path

join

input_path

’*csv‘

)):

# 此處把 sales_* 修改為 *csv

row_counter

=

1

with

open

input_file

’r‘

newline

=

’‘

as

csv_in_file

filereader

=

csv

reader

csv_in_file

header

=

next

filereader

None

for

row

in

filereader

row_counter

+=

1

print

{0!s}

\t

{1:d}

rows

\t

{2:d}

columns‘

format

os

path

basename

input_file

),

row_counter

len

header

)))

file_counter

+=

1

print

’Number of files:

{0:d}

format

file_counter

))

然後開啟終端,導航至 8csv_reader_counts_for_multiple_files。py 所在的資料夾,並輸入如下命令:

python3 8csv_reader_counts_for_multiple_files。py /Users/jason/Documents/GitHub/NoteforPythonDataAnalyze/房地產開發投資情況/csvFile

此時終端就會輸出每個 csv 檔案有多少行多少列,最後會輸出一共有多少檔案。

2。7 從多個檔案中連線資料

實際處理資料時,可能會有多個檔案,這些檔案的內容格式一致,需要放在一起進行資料統計工作,比如全國各地提交給統計局的房地產資料,它們可能是“北京。csv”,“上海。csv”,“廣東。csv”這樣的命名方式。

在原書中是將三個 sales 檔案的資料合併到一起,我會在這裡貼上原始碼,同時附上我們自己修改後的程式碼。

基礎 Python

先附上原始碼:

import

csv

import

glob

import

os

import

sys

input_path

=

sys

argv

1

output_file

=

sys

argv

2

first_file

=

True

for

input_file

in

glob

glob

os

path

join

input_path

’sales_*‘

)):

print

os

path

basename

input_file

))

# 列印當前處理檔案的名稱

with

open

input_file

’r‘

newline

=

’‘

as

csv_in_file

with

open

output_file

’a‘

newline

=

’‘

as

csv_out_file

filereader

=

csv

reader

csv_in_file

filewriter

=

csv

writer

csv_out_file

if

first_file

for

row

in

filereader

filewriter

writerow

row

first_file

=

False

else

header

=

next

filereader

None

for

row

in

filereader

filewriter

writerow

row

根據上面的程式碼,我們試試把房地產開發投資情況中的各個 csv 檔案進行一下整合,要求如下:

所有的 csv 檔案中的資料需要整合到一張 csv 表裡面;

整合後的資料需要能夠看得出來是哪個省份在哪個時間的房地產資料;

對於單獨的每個 csv 檔案開頭幾行的資料庫地區時間這些,要剔除掉,最後一排的資料來源,每個表裡面都有,為了統計的需要,也不用每個都保留下來。

那麼我們先開啟其中任意一個 csv 檔案來看看,它都有些什麼資料,開啟的方式並不是直接雙擊這個 csv 檔案,因為我們要考慮到在實際工作中,我們可能要開啟十幾個以 GB 為單位的 csv 檔案,同時也為了練習一下剛才的程式碼,我們試試用 Python 來開啟並輸出其中一個檔案,此處就挑選內蒙古。csv 這個檔案,那麼程式碼可以這樣寫:

import

csv

import

sys

input_file

=

sys

argv

1

with

open

input_file

as

csv_in_file

filereader

=

csv

reader

csv_in_file

for

row

in

filereader

for

cell

in

row

if

cell

print

cell

end

=

\t

print

’‘

列印下來的資料大概長這個樣子:

| 資料庫:分省月度資料 | | | | | | | —————————————— | —————— | —————— | —————— | —— | ————- | | 地區:內蒙古自治區 | | | | | | | 時間:最近36個月 | | | | | | | 指標 | 2019年12月 | 2019年11月 | 2019年10月 | 。。。 | 2017年1月 | | 房地產投資累計值(億元) | 1041。95 | 1026。08 | 957。6 | 。。。 | | | 房地產投資累計增長(%) | 18 | 17。2 | 15。8 | 。。。 | | | 房地產住宅投資累計值(億元) | 782。13 | 769。18 | 702。77 | 。。。 | | | 房地產住宅投資累計增長(% | 21。8 | 20。7 | 16。9 | 。。。 | | | 資料來源:國家統計局 | | | | | |

在原始資料中,內蒙古剛好沒有 2917 年 1 月的相關房地產資料,這裡也給我們提了個醒,對於資料的整理工作,務必要注意,並不是每個單元格都是有資料的,也並不是每個單元格的資料都是正確的。

好了,開始正式的程式碼:

import

csv

import

glob

import

os

import

sys

import

re

input_path

=

sys

argv

1

output_file

=

sys

argv

2

first_file

=

True

for

input_file

in

glob

glob

os

path

join

input_path

’*csv‘

)):

message

=

“Dealing with file ”

+

str

os

path

basename

input_file

))

# print(message)

with

open

input_file

’r‘

newline

=

’‘

as

csv_in_file

with

open

output_file

’a‘

newline

=

’‘

as

csv_out_file

filereader

=

csv

reader

csv_in_file

filewriter

=

csv

writer

csv_out_file

row_counter

=

1

if

first_file

for

row

in

filereader

if

row_counter

>

3

and

row_counter

<

9

# 對於第一個處理的檔案,前 3 行和第 9 行資料是不需要的,只保留從第 4 行開始的資料

if

row_counter

==

4

row

insert

0

“地區”

# 在第 4 行的開頭插入一個地區列,用來區分這些資料是哪個省市的

else

pattern

=

re

compile

“\。csv”

# 用正則表示式把檔名的 。csv 去掉,把檔名加入到地區那一列

location_name

=

re

sub

pattern

’‘

os

path

basename

input_file

))

row

insert

0

location_name

filewriter

writerow

row

row_counter

+=

1

first_file

=

False

else

for

row

in

filereader

if

row_counter

>

4

and

row_counter

<

9

pattern

=

re

compile

“\。csv”

location_name

=

re

sub

pattern

’‘

os

path

basename

input_file

))

row

insert

0

location_name

filewriter

writerow

row

row_counter

+=

1

pandas 連線多個檔案

首先我還是貼出書中的原始碼:

import

pandas

as

pd

import

glob

import

os

import

sys

input_path

=

sys

argv

1

output_file

=

sys

argv

2

all_files

=

glob

glob

os

path

join

input_path

’sales_*‘

))

all_data_frames

=

[]

for

file

in

all_files

data_frame

=

pd

read_csv

file

index_col

=

None

all_data_frames

append

data_frame

data_frame_concat

=

pd

concat

all_data_frames

axis

=

0

ignore_index

=

True

data_frame_concat

to_csv

output_file

index

=

False

書中第 78 頁提到,這段程式碼是垂直堆疊資料框,如果需要平行連線資料,那麼就在 concat 函式中設定 axis=1。我想到了一個平行連線資料的實際需求,試想一下,如果要追蹤一群人每年的某些資料,比如 NBA 球星在每年的三分球,場均得分,助攻等等資料,而他們每年的資料是按年列的 csv 表格,就可能需要按平行連線資料,各位看官可以自己試試看。

貼出了原始碼,我仍然不會按照這段程式碼去執行,而是用 pandas 來整合房地產的資料,下面請看我自己修改過後的程式碼:

import

pandas

as

pd

import

glob

import

os

import

sys

import

re

finishwriting

=

“Finish writing data to file”

input_path

=

sys

argv

1

output_file

=

sys

argv

2

all_house_price_files

=

glob

glob

os

path

join

input_path

’*csv‘

))

all_data_frames

=

[]

first_file

=

True

for

house_price_file

in

all_house_price_files

if

first_file

“”“以下程式碼是用來獲取當前處理 csv 檔案的檔名”“”

district_names

=

[]

file_name

=

os

path

basename

house_price_file

pattern

=

re

compile

’\。csv‘

district_name

=

re

sub

pattern

’‘

file_name

“”“還記得之前的 drop 函式和 iloc 函式麼,又用到他們來挑選指定行了”“”

data_frame

=

pd

read_csv

house_price_file

header

=

None

data_frame

=

data_frame

drop

([

0

1

2

8

])

data_frame

columns

=

data_frame

iloc

0

data_frame

=

data_frame

reindex

data_frame

index

drop

3

))

“”“為了知道彙總後的資料都是哪個省市的房地產資料,需要提前插入一列地區”“”

for

row

in

range

data_frame

shape

0

]):

district_names

append

district_name

data_frame

insert

0

’地區‘

district_names

data_frame

to_csv

output_file

mode

=

’a‘

index

=

None

encoding

=

’utf-8-sig‘

# 需要注意,可能由於一些相容性問題,我的電腦上編碼居然是 utf-8-sig,不然可能出現寫入檔案亂碼

print

data_frame

first_file

=

False

else

district_names

=

[]

file_name

=

os

path

basename

house_price_file

pattern

=

re

compile

’\。csv‘

district_name

=

re

sub

pattern

’‘

file_name

data_frame

=

pd

read_csv

house_price_file

header

=

None

data_frame

=

data_frame

drop

([

0

1

2

3

8

])

for

row

in

range

data_frame

shape

0

]):

district_names

append

district_name

data_frame

insert

0

’地區‘

district_names

data_frame

to_csv

output_file

mode

=

’a‘

index

=

None

header

=

None

encoding

=

’utf-8-sig‘

# mode=’a‘ 意思就是向檔案中以附加的方式寫入資料,而不是覆蓋寫入

print

finishwriting

原始程式碼放在這裡,您也可以查詢對應寫入好的 pandas_concat_rows_from_multiple_files_in_房地產開發投資情況。csv 檔案,簡單說一下我在寫這段程式碼時遇到的問題,僅供各位參考:

每個省份或直轄市的資料是單獨存放在不同的 csv 檔案中的,如果我把它們整合到一起,那麼會不知道哪個資料屬於哪個省份,此時需要在對應資料的前面加一列代表省份或直轄市;

把所有資料合併到一起時,可能會出現有多個標題列,這並不是我們想看到的,可以設定一個 first_file = True 的旗標,當輸入為第一個檔案時,我們儲存這個標題列,當輸入不是第一個檔案時,我們用 header=None 來忽略掉標題列,只保留相應資料;

選擇合併的方法並不唯一,可以把所有資料都先整合到一個 all_data_frames 中再統一寫入檔案,也可以分別寫入資料,注意分別寫入時,data_frame。to_csv 的限定條件有一個 mode=’a‘,代表附加寫入,而不是覆蓋寫入;

亂碼問題,解決這個問題的最好辦法其實是在 csv 檔案中不要包含中文,因為 python 本身對中文的支援並不是很好,當然我們都知道,這在實際應用中是不可能的,你無法要求錄入資料的人一個漢字都不寫,所以要在處理資料時注意解碼問題。

2。8 計算每個檔案中值的總和與均值

有時候我們會收到很多檔案格式一致的資料,要求計算出裡面某個列的總和,對於單個檔案來講,我們可以直接使用一些內建的 Excel 函式,但是如果有多個檔案則會更復雜,比如某公司某年所有銷售點的營業額總和,所有銷售點營業額的均值,進一步可以計算每個銷售點的盈利能力等等,下面看看用 python 具體如何處理。

基礎 Python

import

csv

import

glob

import

os

import

sys

input_path

=

sys

argv

1

output_file

=

sys

argv

2

output_header_list

=

’file_name‘

’total_sales‘

’average_sales‘

# 建立一個輸出檔案的列標題列表

csv_out_file

=

open

output_file

’a‘

newline

=

’‘

filewriter

=

csv

writer

csv_out_file

filewriter

writerow

output_header_list

# 將標題行寫入輸出檔案

for

input_file

in

glob

glob

os

path

join

input_path

’sales_*‘

)):

with

open

input_file

’r‘

newline

=

’‘

as

csv_in_file

filereader

=

csv

reader

csv_in_file

output_list

=

[]

# 用來儲存要寫入輸出檔案中的每行輸出

output_list

append

os

path

basename

input_file

))

# 您可以試試看,這個地方保留的 input_file 是有後綴 。csv 的,那麼寫入到輸出檔案時怎麼去除後續呢

header

=

next

filereader

# next() 函式去除每個輸入檔案的標題行

total_sales

=

0。0

number_of_sales

=

0。0

for

row

in

filereader

sale_amount

=

row

3

total_sales

+=

float

str

sale_amount

strip

’$‘

replace

’,‘

’‘

))

number_of_sales

+=

1

average_sales

=

{0:。2f}

format

total_sales

/

number_of_sales

output_list

append

total_sales

output_list

append

average_sales

filewriter

writerow

output_list

csv_out_file

close

()

上面這段程式碼儲存為 10csv_reader_sum_average_from_multiple_files。py,已經放在我的 GitHub 倉庫中了,對應的 csv 檔案 10output。csv 也在倉庫中。

pandas 實現

相較於 Python 的基礎 csv 庫,Pandas 提供了 sum() 和 mean() 這兩個摘要統計函式。可參考如下程式碼:

import

pandas

as

pd

import

glob

import

os

import

sys

input_path

=

sys

argv

1

output_file

=

sys

argv

2

all_files

=

glob

glob

os

path

join

input_path

’sales_*‘

))

all_data_frames

=

[]

for

input_file

in

all_files

data_frame

=

pd

read_csv

input_file

index_col

=

None

total_cost

=

pd

DataFrame

([

float

str

value

strip

’$‘

replace

’,‘

’‘

))

for

value

in

data_frame

loc

[:,

’Sale Amount‘

]])

sum

()

average_cost

=

pd

DataFrame

([

float

str

value

strip

’$‘

replace

’,‘

’‘

))

for

value

in

data_frame

loc

[:,

’Sale Amount‘

]])

mean

()

data

=

{

’file_name‘

os

path

basename

input_file

),

’total_sales‘

total_cost

’average_sales‘

average_cost

}

# 原書中這裡寫的是 ’total_sales‘:total_sales 和 ’average_sales‘:average_sales,估計是作者的編譯器自動填充錯了

all_data_frames

append

pd

DataFrame

data

columns

=

’file_name‘

’total_sales‘

’average_sales‘

]))

data_frames_concat

=

pd

concat

all_data_frames

axis

=

0

ignore_index

=

True

data_frames_concat

to_csv

output_file

index

=

False

2。9 本章練習

第 2 章到這裡也就結束了,我們主要學習了對 csv 檔案的各種操作,比如選取特定行列,選取連續行列,讀取多個 csv 檔案,計算總和與均值等等,書中也給我們留下了一些練習。此處不對這些練習進行解析,只把它們寫下來。

對根據具體條件、集合和正則表示式來篩選行資料的一個指令碼進行修改,將與示例程式碼中不同的一組資料打印出來並寫入輸出檔案。

對根據索引值或列標題來篩選列資料的一個指令碼進行修改,將與示例程式碼中不同的一組資料打印出來並寫入輸出檔案。

在一個資料夾中建立一組新的 CSV 輸入檔案,建立另外一個輸出資料夾,使用處理多個檔案的一個指令碼來處理這些新的輸入檔案,並將結果寫入輸出資料夾。

標簽: file  csv  data  Frame  output