最开始的看到的python基础习题之一,现在顺便扩展一点,练习xlrd、pandas以及matplotlib的练习

数据来源

来自于联通官网的通话数据下载,文件为某年某月的通话记录详情,格式为xls**[图片缺失: 2020/06/QQ截图20200627215145.png]**

使用到的库

import os
import xlrd
import re
from functools import reduce
import pandas as pd
import matplotlib.pyplot as plt

具体方法

使用os.listdir()方法得到所有月份的文件路径,每个路径用xlrd.open_workbook()和workbook.sheet_by_index(0)来获取sheet对象,并使用worksheet.nrows和worksheet.row_values( )[ ]方法获取以及定位内容;

使用正则表达式提取内容中的分秒数据并进行计算,进行插入月份操作后得到一个由月份、总时长、通话次数、平均时长的list对象;

对list对象使用pd.DataFrame(list,index=month,columns=[ '月','总时长(秒)','次数','平均(秒/每次)' ])得到DataFrame对象;

对DataFrame对象使用.plot(x=,y=,kind='bar')和plt.show()方法得到直方图

代码

import os
import xlrd
import re
from functools import reduce
import pandas as pd
import matplotlib.pyplot as plt
class tongxun(object):
    def __init__(self,path):
        self.path = path
    def run(self):

        def get_dirs(path):#返回一个由文件完整路径组成的列表
            list_=[]
            for i in os.listdir(path):
                file_path = path + '\\' + i
                list_.append(file_path)
            return list_
        def readxls(path):#从文件中返回一个sheet,输入为文件路径
            workbook = xlrd.open_workbook(path)
            worksheet = workbook.sheet_by_index(0)
            return worksheet
        def get_date(worksheet):#从sheet中获取通话时长列表
            nrows = worksheet.nrows
            time = []
            for i in range(nrows):
                if i == 0:
                    pass
                else:
                    time.append(worksheet.row_values(i)[3])
            return time
        def get_intTime(time_list):#利用正则把分和秒提取出来,输入列表返回列表
            time = []
            for i in time_list:
                s = re.search(r'[0-9]+[分]',i)
                if s == None:
                    m = int(re.search(r'[0-9]+[秒]',i).group().replace('秒',''))
                    time.append(m)
                else:
                    s_m = int(s.group().replace('分',''))
                    m = 60*s_m + int(re.search(r'[0-9]+[秒]',i).group().replace('秒',''))
                    time.append(m)
            return time
        def get_SumNumAvg(time_all):#实现计算,输入为str列表,返回和、总数、平均的int列表
            time = []
            time_every = []
            for i in time_all:
                time.append(get_intTime(i))
            for n in time:
                time_sum = reduce(lambda x,y:x+y,n)
                time_num = len(n)
                time_avg = round(time_sum / time_num,1)
                time_every.append([time_sum,time_num,time_avg])
            return time_every
        def insert_month(SumNumAvg):#列表中插入月份
            c=1
            for i in SumNumAvg:
                i.insert(0,c)
                c += 1
            return SumNumAvg
        def get_dateFrame(time_SumNumAvg):#利用列表生成一个dateFrame对象
            month = list(range(0,len(time_SumNumAvg)))
            df = pd.DataFrame(time_SumNumAvg,index=month,columns=['月','总时长(秒)','次数','平均(秒/每次)'])
            return df
        #此处运行
        time_all = []
        for i in get_dirs(self.path):
            w=readxls(i)
            time_all.append(get_date(w))
        time_SumNumAvg = insert_month(get_SumNumAvg(time_all))
        data=get_dateFrame(time_SumNumAvg)
        while 1:#选择图表类型
            input_ = int(input('\n输入你想要的图表(填对应数字):\n1:总时长\n2:次数\n3:平均数\n'))
            if input_ == 1:
                y_ = '总时长(秒)'
                break
            elif input_ == 2:
                y_ = '次数'
                break
            elif input_ == 3:
                y_ = '平均(秒/每次)'
                break
            else:
                print('输入错误,请重新输入')
        data.plot(x='月',y=y_,kind='bar')
        plt.show()

if __name__ == "__main__":
    y_2020=tongxun(r'C:\Users\14495\Desktop\通信记录')
    y_2020.run()

笔记

xlrd是处理文档的库,xlrd.open_workbook()可以得到一个workbook对象(一个xls文件),wordkbook.sheet_by_index()可以以索引的方式得到一个workbook内的sheet对象,使用worksheet.nrows方法得到一个由sheet内每行内容组成的list,使用for循环遍历提取即可

reduce(function,list)方法可对list中每两个紧挨的元素进行function处理,把得到的结果再与下一位元素进行function处理,以此往复得到一个结果。具体为使用匿名函数lambda以及reduce方法进行求和计算。

round(int,1)方法可得到一个保留一位小数的int数字,具体为求平均通话时长中保留一位小数。

list.insert(data,0)方法可在list的0位置上插入data元素,具体为将月份插入list中每一行的开头。

pandas.DataFrame(list_or_dir,index=month,colums=[ '月','总时长(秒)','次数','平均(秒/每次)' ])得到一个由list或dir为数据、索引为month、list每行对应元素为colums一致的dataframe数据对象。

使用DataFrame对象的.plot(x='月',y=' 平均(秒/每次) ',kind='bar')得到以月份为x轴,以平均数为y轴的直方图。

更新6/28

import os
import xlrd
import re
from functools import reduce
import pandas as pd
import matplotlib.pyplot as plt
import tkinter as tk
from tkinter import filedialog
def get_path():
    win=tk.Tk()
    #filetypes = [('xls files','.xls')]
    answer = filedialog.askdirectory(parent=win,initialdir=os.getcwd(),title='选择一个存放通话文件的文件夹(内容为.xls文件)')
    print(answer)
    return answer
def get_dirs(path):#返回一个由文件完整路径组成的列表
    list_=[]
    for i in os.listdir(path):
        file_path = path + '\\' + i
        list_.append(file_path)
    return list_
def readxls(path):#从文件中返回一个sheet,输入为文件路径
    workbook = xlrd.open_workbook(path)
    worksheet = workbook.sheet_by_index(0)
    return worksheet
#    name = worksheet.name
#    nrows = worksheet.nrows
#    ncols = worksheet.ncols
def get_date(worksheet):#从sheet中获取通话时长列表
    nrows = worksheet.nrows
    time = []
    for i in range(nrows):
        if i == 0:
            pass
        else:
            time.append(worksheet.row_values(i)[3])
    return time
def get_intTime(time_list):
    time = []
    for i in time_list:
        s = re.search(r'[0-9]+[分]',i)
        if s == None:
            m = int(re.search(r'[0-9]+[秒]',i).group().replace('秒',''))
            time.append(m)
        else:
            s_m = int(s.group().replace('分',''))
            m = 60*s_m + int(re.search(r'[0-9]+[秒]',i).group().replace('秒',''))
            time.append(m)
    return time
def get_SumNumAvg(time_all):
    time = []
    time_every = []
    for i in time_all:
        time.append(get_intTime(i))
    for n in time:
        time_sum = reduce(lambda x,y:x+y,n)
        time_num = len(n)
        time_avg = round(time_sum / time_num,1)
        time_every.append([time_sum,time_num,time_avg])
    return time_every
def insert_month(SumNumAvg):
    c=1
    for i in SumNumAvg:
        i.insert(0,c)
        c += 1
    return SumNumAvg
'''
def get_dateFrame(time_SumNumAvg):
    month = list(range(1,len(time_SumNumAvg)+1))
    w = len(time_SumNumAvg[0])
    h = len(time_SumNumAvg)
    t = [[0 for b in range(h)]for d in range(w)]
    count_2 = 0
    for i in time_SumNumAvg:
        count_1 = 0
        for n in i:
            t[count_1][count_2]=n
            count_1 += 1
        count_2 += 1
    df = pd.DataFrame(t,index=['总时长(秒)','次数','平均(秒/每次)'],columns=month)
    return df
'''
def get_dateFrame(time_SumNumAvg):
    month = list(range(0,len(time_SumNumAvg)))
    df = pd.DataFrame(time_SumNumAvg,index=month,columns=['月','总时长(秒)','次数','平均(秒/每次)'])
    return df
def run():
    path = get_path()
    time_all = []
    for i in get_dirs(path):
        w=readxls(i)
        time_all.append(get_date(w))
    time_SumNumAvg = insert_month(get_SumNumAvg(time_all))
    data=get_dateFrame(time_SumNumAvg)
    while 1:#选择图表类型
        input_ = int(input('\n输入你想要的图表(填对应数字):\n1:总时长\n2:次数\n3:平均数\n'))
        if input_ == 1:
            y_ = '总时长(秒)'
            break
        elif input_ == 2:
            y_ = '次数'
            break
        elif input_ == 3:
            y_ = '平均(秒/每次)'
            break
        else:
            print('输入错误,请重新输入')
    data.plot(x='月',y=y_,kind='bar')
    plt.show()

if __name__ == "__main__":
    run()

加入了TK用于文件对话框返回一个路径

由于面向对象写的很烂,暂时先去掉了,之后打算弄个图形界面