在數據處理過程中,可能會需要採用篩選、提取、重新生成等方法來對數據進行處理,下面以csv文件為例,介紹使用Python處理文本文件的方法。
開始之前在面對文件讀寫類的問題時,open函數是一定繞不開的,下面先介紹幾種open函數使用的技巧。
file_path = 'test.csv'
# bad
fp = open(file_path, 'r')
fp.read()
fp.close()
# good
with open(file_path, 'r') as fp:
fp.read()
# bad
with open(file_path, 'r') as fp:
lines = fp.readlines()
for index in range(lines):
print(lines[index])
# good
with open(file_path, 'r') as fp:
for line in fp:
print(line)
# bad
with open(file1, 'r') as fp1:
with open(file2, 'w') as fp2:
for line in fp1:
fp2.write(line)
# good
with open(file1, 'r') as fp1, open(file2, 'w') as fp2:
for line in fp1:
fp2.write(line)
csv文件是一類出現頻率非常高的文本文件,在python中有許多方法可以讀寫他。下面介紹兩種讀寫的方法。
在開始之前,先來介紹一下csv文件的格式,比如下面這個文件,裡面有4行,第一行是表頭或者叫列名(可以沒有表頭),每一列之間使用,隔開。後面3列是對應的內容,也是使用,進行隔開;每一行都是單獨的,行與行之間使用\n進行分割,這個\n是一個不可見字符,作用是換行。
name,age,city
liming,12,taian
zhangsan,20,heze
lisi,18,shenzhen
原生方法是指不藉助第三方庫,只使用python本身來進行讀寫。
根據上面我們對csv文件的了解,我們可以得到下面這些信息
第一行可能是表頭,也可能直接就是數據
列與列之間使用,進行分割
行與行之間使用\n進行分割
下面來編寫函數來進行解析csv文件,我們需要一個文件路徑csv_file來進行讀取,需要分隔符sep,來確定列與列之間是由什麼來進行分割的,最後還需要一個header來控制是否在最後的數據中保留第一行(因為第一行可能是表頭,不是真正的數據)
def parse_csv(csv_file, sep=',', header=False):
result = [] # 存放最終的數據
with open(csv_file, 'r') as fp:
# 如果是True,那麼可以跳過第一行
if header:
fp.readline() # 直接丟棄(讀取)第一行
for line in fp:
result.append(line.split(sep)) # 根據sep來進行分割
return result
pandas是python中一個十分強大的數據分析工具,下面使用pandas來實現和上面代碼一樣的功能
import pandas as pd
result = pd.read_csv(csv_file, sep=',', header=None) # 包括第一行
result = pd.read_csv(csv_file, sep=',') # 不包括第一行
可以發現,兩種方式的代碼量相差很大,第二種方法幾乎是一行代碼就可以實現csv的讀取。上面兩種方法都是讀取的操作,下面來介紹寫入的操作
寫入原生方法在寫入的時候,我們只需要構造出符合csv文件格式的字符串,然後寫入文件即可
將每一列的數據使用分隔符sep進行連接,將每一行之間的數據使用\n進行分割
data = [
['liming','12','taian'],
['zhangsan','20','heze'],
['lisi','18','shenzhen'],
]
def write_csv(data, output_file, sep=','):
with open(output_file, 'w') as fp:
for line in data:
# 使用sep將每一行中的數據連接起來
# 同時在最後加上\n來進行換行
fp.write(sep.join(line) + '\n')
import pandas as pd
data = [
['liming', '12', 'taian'],
['zhangsan', '20', 'heze'],
['lisi', '18', 'shenzhen'],
]
df = pd.DataFrame(data) # 需要先構造一個數據表
# 把構造的數據表存下來,不存表頭,不存索引
df.to_csv(output_file, header=None, index=None)
下面我將從一個具體的案例來講述如何使用python來完成文本處理的任務。
案例講解需求有一個csv文件,第一列是gene_id,第二列是表達量
現在你需要在裡面篩選20個特定的基因,同時根據另一個文件為這20個gene_id重命名
最後將篩選出來的gene_id和expression保存到一個新的csv文件裡面
下面我們也是使用兩種方法來完成這個需求
原生方法def get_geneid_map(geneid_map_csv):
""" 將第二個csv文件轉化從dict中,舊id做key,新id做value """
result = {}
with open(geneid_map_csv,'r') as fp:
fp.readline() # 去掉表頭
for line in fp:
line = line.strip('\n') # 去掉最後的\n
line = line.split(',') # 根據,進行分割
result[line[0]] = line[1] # 存到字典裡面
return result
def parse_gene_file(gene_file,geneid_map_dict):
result = []
with open(gene_file,'r') as fp:
fp.readline() # 去掉表頭
for line in fp:
line = line.strip('\n')
line = line.split(',')
if line[0] in geneid_map_dict:
result.append([
geneid_map_dict[line[0]], # 根據舊id獲得新id
line[1] # 表達量
])
return result
def save_to_csv(result,output_file):
with open(output_file,'w') as fp:
for line in result:
fp.write(','.join(line) + '\n')
if __name__ == '__main__':
csv_file1 = 'gene.csv'
csv_file2 = 'geneid_map.csv'
csv_result = 'filter.csv'
geneid_map = get_geneid_map(csv_file2)
result = parse_gene_file(csv_file1,geneid_map)
save_to_csv(result,csv_result)
if __name__ == '__main__':
csv_file1 = 'gene.csv'
csv_file2 = 'geneid_map.csv'
csv_result = 'filter.csv'
import pandas as pd
# 讀取csv文件,數據不包含header
gene = pd.read_csv(csv_file1,sep=',')
geneid = pd.read_csv(csv_file2,sep=',')
# 使用isin篩選需要的id,同時拷貝一份
filter_gene = gene[gene['gene_id'].isin(geneid['gene_id1'])].copy()
# 構造字典,與上面構造的字典相同
geneid_map = {v['gene_id1']:v['gene_id2'] for k,v in geneid.to_dict('index').items()}
# 使用字典對舊id進行替換
filter_gene['gene_id'] = filter_gene['gene_id'].map(geneid_map)
# 保存csv,不包含表頭,不包含索引
filter_gene.to_csv(csv_result,header=None,index=None)
後臺回復csv即可獲得案例中使用的文件