分类目录归档:Python 数据分析

如何使用Python预测机票价格

印度的机票价格基于供需关系浮动,很少受到监管机构的限制。因此它通常被认为是不可预测的,而动态定价机制更增添了人们的困惑。

我们的目的是建立一个机器学习模型,根据历史数据预测未来航班的价格,这些航班价格可以给客户或航空公司服务提供商作为参考价格。

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。

如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda,它内置了Python和pip.

此外,推荐大家用VSCode编辑器来编写小型Python项目:Python 编程的最好搭档—VSCode 详细指南

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),输入命令安装依赖:

pip install pandas
pip install numpy
pip install matplotlib
pip install seaborn
pip install scikit-learn

2.导入相关数据集

本文的数据集是 Data_Train.xlsx,首先看看训练集的格式:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style('whitegrid')


flights = pd.read_excel('./Data_Train.xlsx')
flights.head()

可见训练集中的字段有航空公司(Airline)、日期(Date_of_Journey)、始发站(Source)、终点站(Destination)、路线(Route)、起飞时间(Dep_Time)、抵达时间(Arrival_Time)、历经时长(Duration)、总计停留站点个数(Total_Stops)、额外信息(Additional_Info),最后是机票价格(Price)。

与其相对的测试集,除了缺少价格字段之外,与训练集的其他所有字段均一致。

访问:https://pythondict.com/download/predict-ticket/

或在Python实用宝典后台回复:预测机票,下载完整数据源和代码。

3.探索性数据分析

3.1 清理缺失数据

看看所有字段的基本信息:

flights.info()

其他的非零值数量均为10683,只有路线和停靠站点数是10682,说明这两个字段缺少了一个值。

谨慎起见,我们删掉缺少数据的行:

# clearing the missing data
flights.dropna(inplace=True)
flights.info()

现在非零值达到一致数量,数据清理完毕。

3.2 航班公司分布特征

接下来看看航空公司的分布特征:

sns.countplot('Airline', data=flights)
plt.xticks(rotation=90)
plt.show()

前三名的航空公司分别是 IndiGo, Air India, JetAirways.

其中可能存在廉价航空公司。

3.3 再来看看始发地的分布

sns.countplot('Source',data=flights)
plt.xticks(rotation=90)
plt.show()

某些地区可能是冷门地区,存在冷门机票的可能性比较大。

3.4 停靠站点的数量分布

sns.countplot('Total_Stops',data=flights)
plt.xticks(rotation=90)
plt.show()

看来大部分航班在飞行途中只停靠一次或无停靠。

会不会某些停靠多的航班比较便宜?

3.5 有多少数据含有额外信息

plot=plt.figure()
sns.countplot('Additional_Info',data=flights)
plt.xticks(rotation=90)

大部分航班信息中都没有包含额外信息,除了部分航班信息有:不包含飞机餐、不包含免费托运。

这个信息挺重要的,是否不包含这两项服务的飞机机票比较便宜?

3.6 时间维度分析

首先转换时间格式:

flights['Date_of_Journey'] = pd.to_datetime(flights['Date_of_Journey'])
flights['Dep_Time'] = pd.to_datetime(flights['Dep_Time'],format='%H:%M:%S').dt.time

接下来,研究一下出发时间和价格的关系:

flights['weekday'] = flights[['Date_of_Journey']].apply(lambda x:x.dt.day_name())
sns.barplot('weekday','Price',data=flights)
plt.show()

大体上价格没有差别,说明这个特征是无效的。

那么月份和机票价格的关系呢?

flights["month"] = flights['Date_of_Journey'].map(lambda x: x.month_name())
sns.barplot('month','Price',data=flights)
plt.show()

没想到4月的机票价格均价只是其他月份的一半,看来4月份是印度的出行淡季吧。

起飞时间和价格的关系

flights['Dep_Time'] = flights['Dep_Time'].apply(lambda x:x.hour)
flights['Dep_Time'] = pd.to_numeric(flights['Dep_Time'])
sns.barplot('Dep_Time','Price',data=flights)
plot.show()

可以看到,红眼航班(半夜及早上)的机票比较便宜,这是符合我们的认知的。

3.7 清除无效特征

把那些和价格没有关联关系的字段直接去除掉:

flights.drop(['Route','Arrival_Time','Date_of_Journey'],axis=1,inplace=True)
flights.head()

4.模型训练

接下来,我们可以准备使用模型来预测机票价格了,不过,还需要对数据进行预处理和特征缩放。

4.1 数据预处理

将字符串变量使用数字替代:

from sklearn.preprocessing import LabelEncoder
var_mod = ['Airline','Source','Destination','Additional_Info','Total_Stops','weekday','month','Dep_Time']
le = LabelEncoder()
for i in var_mod:
    flights[i] = le.fit_transform(flights[i])
flights.head()

对每列数据进行特征缩放,提取自变量(x)和因变量(y):

flights.corr()
def outlier(df):
    for i in df.describe().columns:
        Q1=df.describe().at['25%',i]
        Q3=df.describe().at['75%',i]
        IQR= Q3-Q1
        LE=Q1-1.5*IQR
        UE=Q3+1.5*IQR
        df[i]=df[i].mask(df[i]<LE,LE)
        df[i]=df[i].mask(df[i]>UE,UE)
    return df
flights = outlier(flights)
x = flights.drop('Price',axis=1)
y = flights['Price']

划分测试集和训练集:

from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=101)

4.2 模型训练及测试

使用随机森林进行模型训练:

from sklearn.ensemble import RandomForestRegressor
rfr=RandomForestRegressor(n_estimators=100)
rfr.fit(x_train,y_train)

在随机森林中,我们有一种根据数据的相关性来确定特征重要性的方法:

features=x.columns
importances = rfr.feature_importances_
indices = np.argsort(importances)
plt.figure(1)
plt.title('Feature Importances')
plt.barh(range(len(indices)), importances[indices], color='b', align='center')
plt.yticks(range(len(indices)), features[indices])
plt.xlabel('Relative Importance')

可以看到,Duration(飞行时长)是影响最大的因子。

对划分的测试集进行预测,得到结果:

predictions=rfr.predict(x_test)
plt.scatter(y_test,predictions)
plt.show()

这样看不是很直观,接下来我们要数字化地评价这个模型。

4.3 模型评价

sklearn 提供了非常方便的函数来评价模型,那就是 metrics :

from sklearn import metrics
print('MAE:', metrics.mean_absolute_error(y_test, predictions))
print('MSE:', metrics.mean_squared_error(y_test, predictions))
print('RMSE:', np.sqrt(metrics.mean_squared_error(y_test, predictions)))
print('r2_score:', (metrics.r2_score(y_test, predictions)))
MAE: 1453.9350628905618
MSE: 4506308.3645551
RMSE: 2122.806718605135
r2_score: 0.7532074710409375

这4个值中你可以只关注R2_score,r2越接近1说明模型效果越好,这个模型的分数是0.75,算是很不错的模型了。

看看其残差直方图是否符合正态分布:

sns.distplot((y_test-predictions),bins=50)
plt.show()

不错,多数预测结果和真实值都在-1000到1000的范围内,算是可以接受的结果。其残差直方图也基本符合正态分布,说明模型是有效果的。

部分译自 https://www.kaggle.com/harikrishna9/how-to-predict-flight-ticket-price/notebook,有较多的增删。

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!

给作者打赏,选择打赏金额
¥1¥5¥10¥20¥50¥100¥200 自定义

​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Python 利用股市找到那些存在问题的上市企业

当某个股票保持下跌的时候,你可以肯定该股票一定有什么地方不对,要么是它的市场,要么是它的公司。——利弗莫尔

因此结合自己最近的研究,提出了这个比较新颖的想法:利用股票市场找到已有或潜在食品安全问题的企业。

为什么会有这个想法呢?我们知道食品安全事故发生的时间点和媒体报道的时间点之间实际上是有一个间隔的,然而相关利益人士、内部人士却能提前知道事件的发生。

比如说,2012年11月19日,酒鬼酒被国家质检总局爆出塑化剂超标247%,但是在19号之前,酒鬼酒的收盘价却神奇地从2012年11月2日的55元回落到2012年11月16号的46元。然而国家质检总局的消息是在2012年11月19号才发布的。

而且这样的下跌,明显违背该股票当时上涨的趋势,在10月底时,它的股价已突破周K的压力线,却很不自然地下跌了?而且还违背了许多技术指标,如1号、14号、16号,KDJ和BOLL指标明显提示上涨,在股市这种情绪化的市场中,有人却不为所动,仍然售出大量股票导致其不正常下跌。

2012年11月19号消息公布时紧急停牌,复牌后有三个跌停,股价暴跌48%,但我相信某些人已经成功躲开了这场股灾。

找到已有或潜在食品安全问题的企业的重点在于两个方面:

1.该股票在该板块或者该股强走势的情况下却发生连续多日的下跌

2.不正常的跌停板

如果我们在消息公布/(不公布)前能提前捕捉到这个异常信息,我们就能提前捕捉到某个食品的安全问题,而且也能规避投资风险。当然,出现这种异常的可能性非常多,这种异常只能作为参考。

下面让我们尝试用Python来找到第一种趋势的企业,当然我最后不会公布结果,大家感兴趣可以自己试试:

首先利用tushare找到和食品安全相关的上市企业:

import tushare as ts
def food_codes():
    data = ts.get_industry_classified()
    print data[data.c_name.isin(['食品行业','农药化肥','酿酒行业'])]

得到结果:

均线是我们获得该股票趋势的基础,下面我们编写均线函数:

def get_ma(code,start='',end=datetime.date.today().strftime("%Y-%m-%d")):
    data = ts.get_k_data(code)
    data = data.sort_index(ascending=False)
    data['ma2'] = data['close'].rolling(2).mean().shift(-1)
    data['ma5'] = data['close'].rolling(5).mean().shift(-4)
    data['ma10'] = data['close'].rolling(10).mean().shift(-9)
    data['ma20'] = data['close'].rolling(20).mean().shift(-19)
    data['ma60'] = data['close'].rolling(60).mean().shift(-59)
    data['ma240'] = data['close'].rolling(240).mean().shift(-239)
    data['date'] = pd.to_datetime(data['date'])
    if start == '':
        return data
    start = pd.to_datetime(start)
    end = pd.to_datetime(end)
    if data['date'][len(data) - 1] < start:
        return 0
    while data.loc[data.date == start].empty:
        start = start + dateutil.relativedelta.relativedelta(days=1)
    while data.loc[data.date == end].empty:
        end = end - dateutil.relativedelta.relativedelta(days=1)
    return data.loc[(data.date >= start) & (data.date <= end)]

我们只需要确定两点:

1. 以20个交易日为窗口期,其内最高价和最低价的差距大于其最高价的15%。

2. 该股票处于涨势。

def analyzeOne(code):
    rng = pd.date_range('2018-1-1', datetime.date.today().strftime("%Y-%m-%d"), freq='D')
    # 获得日期
    flag = 0
    for i in range(20,len(rng)):
        data = get_ma(code,rng[i-20],rng[i])
        count = 0
        data = data.sort_index(ascending=True)
        # 升序
        max = data['close'][data['close'].argmax()]
        min = data['close'][data['close'].argmin()]
        for j in range(len(data)):
            if data.iloc[j]['ma5'] >= data.iloc[j]['ma20'] and data.iloc[j]['ma20'] >= data.iloc[j]['ma60'] and \
                    data.iloc[j]['ma60'] >= data.iloc[j]['ma240']:
                count = count + 1
                if count >= 5 and (max-min) - max*0.15 > 0:
                    print 'Code: ' + str(code) +', Problem:' + str(rng[i-20])+ ' ' + str(rng[i])
            else:
                count = 0

如果存在这样的趋势,就输出这个趋势的窗口时间段,当然这只是一个粗略的模型,结果中可能会有很多意外的情况,但是我觉得足够启发大家了。

想要应用于所有食品相关股票:

def find_down():
    for i in food_codes().code:
        analyzeOne(i)

通过这样的操作,我找到了两支类似的股票:

但这些股票都没有新闻报道发生了问题,最后也让我怀疑自己的模型的准确性,到底是市场正常波动,还是这个企业存在问题。

因此,这个模型存在许多的优化空间,如何通过股市最准确地找出那些出问题的企业,还有待进一步研究,本实验只是提出一个初步的模型和一些想法。

个人愚见,欢迎讨论。

我们的文章到此就结束啦,如果你喜欢今天的Python 实战教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应红字验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!

点击下方阅读原文可获得更好的阅读体验

Python实用宝典 (pythondict.com)
不只是一个宝典
欢迎关注公众号:Python实用宝典

Python 2015-2020年美国警察致命枪击案EDA分析

2014年在密苏里州一名叫做弗格森(Ferguson)的警察杀害了迈克尔·布朗(Michael Brown)后,美国黑人开始了一场抗议警察暴力对待黑人的运动—Black Lives Matter(黑人的命也是命,简称BLM)。

2020年,在明尼阿波利斯警察Derek Chauvin杀害乔治·弗洛伊德(George Floyd)之后,BLM运动再次成为头条新闻,引起国际社会的进一步关注。

自2015年1月1日起,《华盛顿邮报》一直在整理一个数据库,其中记录了值班警员在美国发生的每起致命枪击事件。这个数据库里包含了死者的种族,年龄和性别,该人是否有武器,以及受害人是否正在遭受精神健康危机。

此外,还有四个其他数据集。有关贫困率,高中毕业率,家庭收入中位数和种族人口统计数据的美国人口普查数据。

下面就让我们来使用这些数据集来进行数据分析。

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda,它内置了Python和pip.

此外,推荐大家用VSCode编辑器,因为它可以在编辑器下方的终端运行命令安装依赖模块:Python 编程的最好搭档—VSCode 详细指南。

本文具备流程性,建议使用 VSCode 的 Jupiter Notebook 扩展,新建一个名为 test.ipynb 的文件,跟着教程一步步走下去。

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),准备开始输入命令安装依赖。

所需依赖:

pip install numpy
pip install pandas
pip install plotly
pip install seaborn

本文译自:https://www.kaggle.com/edoardo10/fatal-police-shooting-eda-plotly-seaborn/data,如需数据请在公众号后台回复:警察枪击EDA

2.代码与分析

首先,引入我们分析所需要使用的模块:

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
from datetime import datetime
import plotly.express as px
import plotly.graph_objects as go
import warnings
import plotly.offline as pyo
pyo.init_notebook_mode()
warnings.filterwarnings('ignore')
pd.set_option('display.max_columns', 500)
sns.set_style('white')
%matplotlib inline

打开需要分析的数据集:

df = pd.read_csv('./PoliceKillingsUS.csv', encoding='cp1252')
df.head()

时间特征

从这6年的月度数据来看,我们可以看到,在2015年上半年、2018年初和2020年第一季度,我们达到了每月超过100起致命事故的高峰。从月度来看,这种现象不具备明显的季节性。

df['date'] = df['date'].apply(lambda x: pd.to_datetime(x))
df['date'].groupby(df.date.dt.to_period('M')).count().plot(kind='line')

看看警察枪击案的事故是否具有周末特征:

count = df['date'].apply(lambda x: 'Weekday' if x.dayofweek < 5 else 'Weekend').value_counts(normalize=True)
f, ax = plt.subplots(1,1)
sns.barplot(x=count.index, y=count.values, ax=ax, palette='twilight')

显然,我们没有证据表明周末会发生更多的案件。

不过,如果细化到星期里的每一天,我们会发现周中发生案件的概率较高:

count = df['date'].apply(lambda x: x.dayofweek).value_counts(normalize=True).sort_index()
count.index = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun']
f, ax = plt.subplots(1,1)
sns.barplot(x=count.index, y=count.values, ax=ax, palette='twilight')
ax.set_title('Cases (%) for each day of the week');

接下来看看以下4个特征的分布:

signs_of_mental_illness:是否精神不稳定
threat_level:威胁等级
body_camera:警察是否带了随身摄像头
manner_of_death:死亡方式
count_1 = df['signs_of_mental_illness'].value_counts(normalize=True)
count_2 = df['threat_level'].value_counts(normalize=True)
count_3 = df['body_camera'].value_counts(normalize=True)
count_4 = df['manner_of_death'].value_counts(normalize=True)
fig, axes = plt.subplots(2, 2, figsize=(8, 8), sharey=True)
sns.barplot(x=count_1.index, y=count_1.values, palette="rocket", ax=axes[0,0])
axes[0,0].set_title('Signs of mental illness (%)')
sns.barplot(x=count_2.index, y=count_2.values, palette="viridis", ax=axes[0,1])
axes[0,1].set_title('Threat level (%)')
sns.barplot(x=count_3.index, y=count_3.values, palette="nipy_spectral", ax=axes[1,0])
axes[1,0].set_title('Body camera (%)')
sns.barplot(x=count_4.index, y=count_4.values, palette="gist_heat", ax=axes[1,1])
axes[1,1].set_title('Manner of death (%)');

我们可以看到,只有20%的案例受害者有精神不稳定的迹象;

只有10%的警察有随身摄像头;

70%的情况被宣布为危险状况;

死亡方式似乎不是一个有趣的变量,因为大多数案件都是“枪毙”;

美国的警察是否具有种族主义倾向?

count = df.race.value_counts(normalize=True)
count.index = ['White', 'Black', 'Hispanic', 'Asian', 'Native American', 'Other']

f, ax = plt.subplots(1,1, figsize=(8,6))
sns.barplot(y=count.index, x=count.values, palette='Reds_r')
ax.set_title('Total cases for each race (%)');

从上图我们知道,大部分致命的枪击事件中,涉及最多的是白人,其次是黑人和西班牙裔。

但这个图表并没有考虑人种比例。参考2019年美国的种族比例,我们可以看到,美国黑人受害者的比例更高:

数据来源:https://data.census.gov/cedsci/table?q=Hispanic%20or%20Latino&tid=ACSDP1Y2019.DP05&hidePreview=false

share_race_usa_2019 = pd.Series([60.0, 12.4, 0.9, 5.6, 18.4, 2.7], index=['White','Black','Native American','Asian','Hispanic','Other'])

count_races = count / share_race_usa_2019
count_races = count_races.sort_values(ascending=False)
f, ax = plt.subplots(1,1, figsize=(8,6))
sns.barplot(y=count_races.index, x=count_races.values, palette='Greens_r')
ax.set_title('Total cases for each race on total USA race percentage rate');

受害者的年龄

sns.set_style('whitegrid')
fig, axes = plt.subplots(1, 1, figsize=(10, 8))
axes.xaxis.set_ticks(np.arange(0,100,10))

sns.kdeplot(df[df.race == 'N'].age, ax=axes, shade=True, color='#7FFFD4')
sns.kdeplot(df[df.race == 'O'].age, ax=axes, shade=True, color='#40E0D0')
sns.kdeplot(df[df.race == 'B'].age, ax=axes, shade=True, color='#00CED1')
sns.kdeplot(df[df.race == 'H'].age, ax=axes, shade=True, color='#6495ED')
sns.kdeplot(df[df.race == 'A'].age, ax=axes, shade=True, color='#4682B4')
sns.kdeplot(df[df.race == 'W'].age, ax=axes, shade=True, color='#008B8B')


legend = axes.legend_
legend.set_title("Race")
for t, l in zip(legend.texts,("Native", "Other", 'Black', 'Hispanic', 'Asian', 'White')):
    t.set_text(l)

由这些叠加的密度图可以看出:

对于亚裔和白人来说,大多数案件的受害者年龄都在30岁左右。

对于其他和印第安人来说,在大多数案件中,受害者大约28岁。

对于西班牙裔和黑人来说,大多数案件的受害者年龄都在25岁左右。

所以我们可以说,西班牙裔和黑人的年轻人,是被警察开枪射击的高危群体。

受害者性别比例

按常理,这种暴力事件的受害者一般都为男性,看看是不是这样:

fig = px.pie(values = df.gender.value_counts(normalize=True).values, names=df.gender.value_counts(normalize=True).index, title='Total cases gender (%)')
fig.update(layout=dict(title=dict(x=0.5),autosize=False, width=400, height=400))
fig.show()

果然如此,超过95%的受害者都为男性。

简单的EDA分析就是这些,作者还分享了许多深层次的分析,不过并没有将数据分享出来,这里就不展示了。

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!

给作者打赏,选择打赏金额
¥1¥5¥10¥20¥50¥100¥200 自定义

​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Python 预测银行信用卡客户是否流失

1.题目背景

越来越多的客户不再使用信用卡服务,银行的经理对此感到不安。如果有人能为他们预测哪些客户即将流失,他们将不胜感激,因为这样他们可以主动向客户提供更好的服务,并挽回这些即将流失的客户。

2.数据集

该数据集由10,000个客户组成,其中包含了他们的年龄,工资,婚姻状况,信用卡限额,信用卡类别等。

不过,这里面只有16%的客户是流失的,因此拿来预测客户是否会流失有点难度。

在Python实用宝典后台回复 预测客户流失 下载这份数据和源代码。

译自kaggle并对原文进行了修改和补充,感谢原作者:

https://www.kaggle.com/thomaskonstantin/bank-churn-data-exploration-and-churn-prediction/

3.代码与分析

开始之前,你要确保Python和pip已经成功安装在电脑上,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda,它内置了Python和pip.

此外,推荐大家用VSCode编辑器,因为它可以在编辑器下方的终端运行命令安装依赖模块:Python 编程的最好搭档—VSCode 详细指南。

本文具备流程性,建议使用 VSCode 的 Jupiter Notebook 扩展,新建一个名为 test.ipynb 的文件,跟着教程一步步走下去。

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),准备开始输入命令安装依赖。

所需依赖:

pip install numpy
pip install pandas
pip install plotly
pip install scikit-learn
pip install scikit-plot

# 这个需要conda
conda install -c conda-forge imbalanced-learn

3.1 导入需要的模块

import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as ex
import plotly.graph_objs as go
import plotly.figure_factory as ff
from plotly.subplots import make_subplots
import plotly.offline as pyo
pyo.init_notebook_mode()
sns.set_style('darkgrid')
from sklearn.decomposition import PCA
from sklearn.model_selection import train_test_split,cross_val_score
from sklearn.ensemble import RandomForestClassifier,AdaBoostClassifier
from sklearn.svm import SVC
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import f1_score as f1
from sklearn.metrics import confusion_matrix
import scikitplot as skplt

plt.rc('figure',figsize=(18,9))
%pip install imbalanced-learn
from imblearn.over_sampling import SMOTE

遇到任何 No module named “XXX” 都可以尝试pip install一下。

如果pip install没解决,可以谷歌/百度一下,看看别人是怎么解决的。

3.2 加载数据

c_data = pd.read_csv('./BankChurners.csv')
c_data = c_data[c_data.columns[:-2]]
c_data.head(3)

这里去掉了最后两列的朴素贝叶斯分类结果。

显示前三行数据, 可以看到所有的字段:

3.3 探索性数据分析

下面看看这20+列数据中,哪一些是对我们有用的。

首先,我想知道数据集中的客户年龄分布:

fig = make_subplots(rows=2, cols=1)

tr1=go.Box(x=c_data['Customer_Age'],name='Age Box Plot',boxmean=True)
tr2=go.Histogram(x=c_data['Customer_Age'],name='Age Histogram')

fig.add_trace(tr1,row=1,col=1)
fig.add_trace(tr2,row=2,col=1)

fig.update_layout(height=700, width=1200, title_text="Distribution of Customer Ages")
fig.show()

可以看到,客户的年龄分布大致遵循正态分布,因此使用可以在正态假设下进一步使用年龄特征。

同样滴,我想知道性别分布如何:

ex.pie(c_data,names='Gender',title='Propotion Of Customer Genders')

可见,在我们的数据集中,女性的样本比男性更多,但是差异的百分比不是那么显著,所以我们可以说性别是均匀分布的。

每个客户的家庭人数的分布怎么样?

fig = make_subplots(rows=2, cols=1)

tr1=go.Box(x=c_data['Dependent_count'],name='Dependent count Box Plot',boxmean=True)
tr2=go.Histogram(x=c_data['Dependent_count'],name='Dependent count Histogram')

fig.add_trace(tr1,row=1,col=1)
fig.add_trace(tr2,row=2,col=1)

fig.update_layout(height=700, width=1200, title_text="Distribution of Dependent counts (close family size)")
fig.show()

可见,它也是大致符合正态分布的,偏右一点。

客户的受教育水平如何?

ex.pie(c_data,names='Education_Level',title='Propotion Of Education Levels')

假设大多数教育程度不明(Unknown)的顾客都没有接受过任何教育。我们可以指出,超过70%的顾客都受过正规教育,其中约35%的人受教育程度达到硕士以上水平,45%的人达到本科以上水准。

他们的婚姻状态如何?

ex.pie(c_data,names='Marital_Status',title='Propotion Of Different Marriage Statuses')

看来,这家银行几乎一半的客户都是已婚人士,有趣的是,另一半客户几乎都是单身人士,另外只有7%的客户离婚了。

看看收入分布和卡片类型的分布:

ex.pie(c_data,names='Income_Category',title='Propotion Of Different Income Levels')
ex.pie(c_data,names='Card_Category',title='Propotion Of Different Card Categories')

可见大部分人的年收入处于60K美元以下。

在持有的卡片的类型上,蓝卡占了绝大多数。

每月账单数量有没有特征?

fig = make_subplots(rows=2, cols=1)

tr1=go.Box(x=c_data['Months_on_book'],name='Months on book Box Plot',boxmean=True)
tr2=go.Histogram(x=c_data['Months_on_book'],name='Months on book Histogram')

fig.add_trace(tr1,row=1,col=1)
fig.add_trace(tr2,row=2,col=1)

fig.update_layout(height=700, width=1200, title_text="Distribution of months the customer is part of the bank")
fig.show()

可以看到中间的峰值特别高,显然这个指标不是正态分布的。

每位客户持有的银行业务数量有没有特征呢?

fig = make_subplots(rows=2, cols=1)

tr1=go.Box(x=c_data['Total_Relationship_Count'],name='Total no. of products Box Plot',boxmean=True)
tr2=go.Histogram(x=c_data['Total_Relationship_Count'],name='Total no. of products Histogram')

fig.add_trace(tr1,row=1,col=1)
fig.add_trace(tr2,row=2,col=1)

fig.update_layout(height=700, width=1200, title_text="Distribution of Total no. of products held by the customer")
fig.show()

基本上都是均匀分布的,显然这个指标对于我们而言也没太大意义。

用户不活跃月份数量有没有好用的特征?

fig = make_subplots(rows=2, cols=1)

tr1=go.Box(x=c_data['Months_Inactive_12_mon'],name='number of months inactive Box Plot',boxmean=True)
tr2=go.Histogram(x=c_data['Months_Inactive_12_mon'],name='number of months inactive Histogram')

fig.add_trace(tr1,row=1,col=1)
fig.add_trace(tr2,row=2,col=1)

fig.update_layout(height=700, width=1200, title_text="Distribution of the number of months inactive in the last 12 months")
fig.show()

这个似乎有点用处,会不会越不活跃的用户越容易流失呢?

信用卡额度的分布如何?

fig = make_subplots(rows=2, cols=1)

tr1=go.Box(x=c_data['Credit_Limit'],name='Credit_Limit Box Plot',boxmean=True)
tr2=go.Histogram(x=c_data['Credit_Limit'],name='Credit_Limit Histogram')

fig.add_trace(tr1,row=1,col=1)
fig.add_trace(tr2,row=2,col=1)

fig.update_layout(height=700, width=1200, title_text="Distribution of the Credit Limit")
fig.show()

大部分人的额度都在0到10k之间,这比较正常,暂时看不出和流失有什么关系。

客户总交易额的分布怎么样?

fig = make_subplots(rows=2, cols=1)

tr1=go.Box(x=c_data['Total_Trans_Amt'],name='Total_Trans_Amt Box Plot',boxmean=True)
tr2=go.Histogram(x=c_data['Total_Trans_Amt'],name='Total_Trans_Amt Histogram')

fig.add_trace(tr1,row=1,col=1)
fig.add_trace(tr2,row=2,col=1)

fig.update_layout(height=700, width=1200, title_text="Distribution of the Total Transaction Amount (Last 12 months)")
fig.show()

这个有点意思,总交易额的分布体现出“多组”分布,如果我们根据这个指标将客户聚类为不同的组别,看他们之间的相似性,并作出不同的画线,也许对我们最终的流失分析有一定的意义。

接下来,最重要的流失用户分布

ex.pie(c_data,names='Attrition_Flag',title='Proportion of churn vs not churn customers')

我们可以看到,只有16%的数据样本代表流失客户,在接下来的步骤中,我将使用SMOTE对流失样本进行采样,使其与常规客户的样本大小匹配,以便给后面选择的模型一个更好的机会来捕捉小细节。

3.4 数据预处理

使用SMOTE模型前,需要根据不同的特征对数据进行One Hot编码:

c_data.Attrition_Flag = c_data.Attrition_Flag.replace({'Attrited Customer':1,'Existing Customer':0})
c_data.Gender = c_data.Gender.replace({'F':1,'M':0})
c_data = pd.concat([c_data,pd.get_dummies(c_data['Education_Level']).drop(columns=['Unknown'])],axis=1)
c_data = pd.concat([c_data,pd.get_dummies(c_data['Income_Category']).drop(columns=['Unknown'])],axis=1)
c_data = pd.concat([c_data,pd.get_dummies(c_data['Marital_Status']).drop(columns=['Unknown'])],axis=1)
c_data = pd.concat([c_data,pd.get_dummies(c_data['Card_Category']).drop(columns=['Platinum'])],axis=1)
c_data.drop(columns = ['Education_Level','Income_Category','Marital_Status','Card_Category','CLIENTNUM'],inplace=True)

显示热力图:

sns.heatmap(c_data.corr('pearson'),annot=True)

3.5 SMOTE模型采样

SMOTE模型经常用于解决数据不平衡的问题,它通过添加生成的少数类样本改变不平衡数据集的数据分布,是改善不平衡数据分类模型性能的流行方法之一。

oversample = SMOTE()
X, y = oversample.fit_resample(c_data[c_data.columns[1:]], c_data[c_data.columns[0]])
usampled_df = X.assign(Churn = y)
ohe_data =usampled_df[usampled_df.columns[15:-1]].copy()
usampled_df = usampled_df.drop(columns=usampled_df.columns[15:-1])
sns.heatmap(usampled_df.corr('pearson'),annot=True)

3.6 主成分分析

我们将使用主成分分析来降低单次编码分类变量的维数,从而降低方差。同时使用几个主成分而不是几十个单次编码特征将帮助我构建一个更好的模型。

N_COMPONENTS = 4

pca_model = PCA(n_components = N_COMPONENTS )

pc_matrix = pca_model.fit_transform(ohe_data)

evr = pca_model.explained_variance_ratio_
cumsum_evr = np.cumsum(evr)

ax = sns.lineplot(x=np.arange(0,len(cumsum_evr)),y=cumsum_evr,label='Explained Variance Ratio')
ax.set_title('Explained Variance Ratio Using {} Components'.format(N_COMPONENTS))
ax = sns.lineplot(x=np.arange(0,len(cumsum_evr)),y=evr,label='Explained Variance Of Component X')
ax.set_xticks([i for i in range(0,len(cumsum_evr))])
ax.set_xlabel('Component number #')
ax.set_ylabel('Explained Variance')
plt.show()
usampled_df_with_pcs = pd.concat([usampled_df,pd.DataFrame(pc_matrix,columns=['PC-{}'.format(i) for i in range(0,N_COMPONENTS)])],axis=1)
usampled_df_with_pcs

特征变得越来越明显:

sns.heatmap(usampled_df_with_pcs.corr('pearson'),annot=True)

4.模型选择及测试

选择出以下特征划分训练集并进行训练:

X_features = ['Total_Trans_Ct','PC-3','PC-1','PC-0','PC-2','Total_Ct_Chng_Q4_Q1','Total_Relationship_Count']

X = usampled_df_with_pcs[X_features]
y = usampled_df_with_pcs['Churn']

train_x,test_x,train_y,test_y = train_test_split(X,y,random_state=42)

4.1 交叉验证

分别看看随机森林、AdaBoost和SVM模型三种模型的表现如何:

rf_pipe = Pipeline(steps =[ ('scale',StandardScaler()), ("RF",RandomForestClassifier(random_state=42)) ])
ada_pipe = Pipeline(steps =[ ('scale',StandardScaler()), ("RF",AdaBoostClassifier(random_state=42,learning_rate=0.7)) ])
svm_pipe = Pipeline(steps =[ ('scale',StandardScaler()), ("RF",SVC(random_state=42,kernel='rbf')) ])


f1_cross_val_scores = cross_val_score(rf_pipe,train_x,train_y,cv=5,scoring='f1')
ada_f1_cross_val_scores=cross_val_score(ada_pipe,train_x,train_y,cv=5,scoring='f1')
svm_f1_cross_val_scores=cross_val_score(svm_pipe,train_x,train_y,cv=5,scoring='f1')
plt.subplot(3,1,1)
ax = sns.lineplot(x=range(0,len(f1_cross_val_scores)),y=f1_cross_val_scores)
ax.set_title('Random Forest Cross Val Scores')
ax.set_xticks([i for i in range(0,len(f1_cross_val_scores))])
ax.set_xlabel('Fold Number')
ax.set_ylabel('F1 Score')
plt.show()
plt.subplot(3,1,2)
ax = sns.lineplot(x=range(0,len(ada_f1_cross_val_scores)),y=ada_f1_cross_val_scores)
ax.set_title('Adaboost Cross Val Scores')
ax.set_xticks([i for i in range(0,len(ada_f1_cross_val_scores))])
ax.set_xlabel('Fold Number')
ax.set_ylabel('F1 Score')
plt.show()
plt.subplot(3,1,3)
ax = sns.lineplot(x=range(0,len(svm_f1_cross_val_scores)),y=svm_f1_cross_val_scores)
ax.set_title('SVM Cross Val Scores')
ax.set_xticks([i for i in range(0,len(svm_f1_cross_val_scores))])
ax.set_xlabel('Fold Number')
ax.set_ylabel('F1 Score')
plt.show()

看看三种模型都有什么不同的表现:

看得出来随机森林 F1分数是最高的。

4.2 模型预测

对测试集进行预测,看看三种模型的效果:

rf_pipe.fit(train_x,train_y)
rf_prediction = rf_pipe.predict(test_x)

ada_pipe.fit(train_x,train_y)
ada_prediction = ada_pipe.predict(test_x)

svm_pipe.fit(train_x,train_y)
svm_prediction = svm_pipe.predict(test_x)

print('F1 Score of Random Forest Model On Test Set - {}'.format(f1(rf_prediction,test_y)))
print('F1 Score of AdaBoost Model On Test Set - {}'.format(f1(ada_prediction,test_y)))
print('F1 Score of SVM Model On Test Set - {}'.format(f1(svm_prediction,test_y)))

4.3 对原始数据(采样前)进行模型预测

​接下来对原始数据进行模型预测:

ohe_data =c_data[c_data.columns[16:]].copy()
pc_matrix = pca_model.fit_transform(ohe_data)
original_df_with_pcs = pd.concat([c_data,pd.DataFrame(pc_matrix,columns=['PC-{}'.format(i) for i in range(0,N_COMPONENTS)])],axis=1)

unsampled_data_prediction_RF = rf_pipe.predict(original_df_with_pcs[X_features])
unsampled_data_prediction_ADA = ada_pipe.predict(original_df_with_pcs[X_features])
unsampled_data_prediction_SVM = svm_pipe.predict(original_df_with_pcs[X_features])

效果如下:

F1最高的随机森林模型有0.63分,偏低,这也比较正常,毕竟在这种分布不均的数据集中,查全率是很难做到很高的。

4.4 结果

让我们看看最终在原数据上使用随机森林模型的运行结果:

ax = sns.heatmap(confusion_matrix(unsampled_data_prediction_RF,original_df_with_pcs['Attrition_Flag']),annot=True,cmap='coolwarm',fmt='d')
ax.set_title('Prediction On Original Data With Random Forest Model Confusion Matrix')
ax.set_xticklabels(['Not Churn','Churn'],fontsize=18)
ax.set_yticklabels(['Predicted Not Churn','Predicted Churn'],fontsize=18)

plt.show()

可见,没有流失的客户命中了7709人,未命中791人。

流失客户命中了1130人,未命中497人。

整体而言,是一个比较优秀的模型了。

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!

给作者打赏,选择打赏金额
¥1¥5¥10¥20¥50¥100¥200 自定义

​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

接近完美的监控系统—普罗米修斯

普罗米修斯(Prometheus)是一个SoundCloud公司开源的监控和告警系统。当年,由于SoundCloud公司生产了太多的服务,传统的监控已经无法满足监控需求,于是他们在2012决定着手开发新的监控系统,即普罗米修斯。

普罗米修斯(下称普罗)的作者 Matt T.Proud 在2012年加入该公司,他从google的监控系统Borgmon中获得灵感,与另一名工程师Julius Volz合作开发了开源的普罗,后来其他开发人员陆续加入到该项目,最终于2015正式发布。

普罗基于Go语言开发,其架构图如下:

其中:

  • Prometheus Server: 用数据的采集和存储,PromQL查询,报警配置。
  • Push gateway: 用于批量,短期的监控数据的汇报总节点。
  • Exporters: 各种汇报数据的exporter,例如汇报机器数据的node_exporter,汇报MondogDB信息的 MongoDB_exporter 等等。
  • Alertmanager: 用于高级通知管理。

1.怎么采集监控数据?

要采集目标(主机或服务)的监控数据,首先就要在被采集目标地方安装采集组件,这种采集组件被称为Exporter。prometheus.io官网上有很多这种exporter:exporter列表,比如:

Consul exporter (official)
Memcached exporter
 (official)
MySQL server exporter
 (official)
Node/system metrics exporter
 (official)
HAProxy exporter
 (official)
RabbitMQ exporter

Grok exporter

InfluxDB exporter
 (official)

等等…

这些exporter能为我们采集目标的监控数据,然后传输给普罗米修斯。这时候,exporter会暴露一个http接口,普罗米修斯通过HTTP协议使用Pull的方式周期性拉取相应的数据。

不过,普罗也提供了Push模式来进行数据传输,通过增加Push Gateway这个中间商实现,你可以将数据推送到Push Gateway,普罗再通过Pull的方式从Push Gateway获取数据。

这就是为什么你从架构图里能看到两个 Pull metrics 的原因,一个是采集器直接被Server拉取数据(pull);另一个是采集器主动Push数据到Push Gateway,Server再对Push Gateway主动拉取数据(pull)。

采集数据的主要流程如下:

1. Prometheus server 定期从静态配置的主机或服务发现的 targets 拉取数据(zookeeper,consul,DNS SRV Lookup等方式)

2. 当新拉取的数据大于配置内存缓存区的时候,Prometheus会将数据持久化到磁盘,也可以远程持久化到云端。

3. Prometheus通过PromQL、API、Console和其他可视化组件如Grafana、Promdash展示数据。

4. Prometheus 可以配置rules,然后定时查询数据,当条件触发的时候,会将告警推送到配置的Alertmanager。

5. Alertmanager收到告警的时候,会根据配置,聚合,去重,降噪,最后发出警告。

2.采集的数据结构与指标类型

2.1 数据结构

了解普罗米修斯的数据结构对于了解整个普罗生态非常重要。普罗采用键值对作为其基本的数据结构:

Key是指标名字,Value是该指标的值,此外Metadata(元信息)也非常重要,也可称之为labels(标签信息)。这些标签信息指定了当前这个值属于哪个云区域下的哪台机器,如果没有labels,数据有可能会被丢失。

2.2 指标类型

普罗米修斯的监控指标有4种基本类型:

1.Counter(计数器):

计数器是我们最简单的指标类型。比如你想统计某个网站的HTTP错误总数,这时候就用计数器。

计数器的值只能增加或重置为0,因此特别适合计算某个时段上某个时间的发生次数,即指标随时间演变发生的变化。

2.Gauges

Gauges可以用于处理随时间增加或减少的指标,比如内存变化、温度变化。

这可能是最常见的指标类型,不过它也有一定缺点:如果系统每5秒发送一次指标,普罗服务每15秒抓取一次数据,那么这期间可能会丢失一些指标,如果你基于这些数据做汇总分析计算,则结果的准确性会有所下滑。

3.Histogram(直方图)

直方图是一种更复杂的度量标准类型。它为我们的指标提供了额外信息,例如观察值的总和及其数量,常用于跟踪事件发生的规模。

比如,为了监控性能指标,我们希望在有20%的服务器请求响应时间超过300毫秒时发送告警。对于涉及比例的指标就可以考虑使用直方图。

4.Summary(摘要)

摘要更高级一些,是对直方图的扩展。除了提供观察的总和和计数之外,它们还提供滑动窗口上的分位数度量。分位数是将概率密度划分为相等概率范围的方法。

对比直方图:

直方图随时间汇总值,给出总和和计数函数,使得易于查看给定指标的变化趋势。

而摘要则给出了滑动窗口上的分位数(即随时间不断变化)。

3.实例概念

随着分布式架构的不断发展和云解决方案的普及,现在的架构已经变得越来越复杂了。

分布式的服务器复制和分发成了日常架构的必备组件。我们举一个经典的Web架构,该架构由3个后端Web服务器组成。在该例子中,我们要监视Web服务器返回的HTTP错误的数量。

使用普罗米修斯语言,单个Web服务器单元称为实例(主机实例)。该任务是计算所有实例的HTTP错误数量。

事实上,这甚至可以说是最简单的架构了,再复杂一点,实例不仅能是主机实例,还能是服务实例,因此你需要增加一个instance_type的标签标记主机或服务。

再再复杂一点,同样的IP,可能存在于不同云区域下,这属于不同的机器,因此还需要一个cloud标签,最终该数据结构可能会变为:

cpu_usage {job=”1″, instance=”128.0.0.1″, cloud=”0″, instance_type=”0″}

4.数据可视化

如果使用过基于InfluxDB的数据库,你可能会熟悉InfluxQL。普罗米修斯也内置了自己的SQL查询语言用于查询和检索数据,这个内置的语言就是PromQL。

我们前面说过,普罗米修斯的数据是用键值对表示的。PromQL也用相同的语法查询和返回结果集。

PromQL会处理两种向量:

即时向量:表示当前时间,某个指标的数据向量。

时间范围向量:表示过去某时间范围内,某个指标的数据向量。

如针对8核CPU的使用率:

知道怎么提取数据后,可视化数据就简单了。

Grafana是一个大型可视化系统,功能强大,可以创建自己的自定义面板,支持多种数据来源,当然也支持普罗米修斯。

通过配置数据源,Grafana会使用相应的SQL拉取并绘制图表,能直接看到普罗米修斯的各个指标数据图表:

更方便的是,Grafana有很多仪表盘模板供你使用,只要import模板进行简单的配置,就能得到以下效果:

5.应用前景

普罗米修斯非常强大,可以应用到各行各业。

5.1 DevOps

为了观察整个服务体系是否在正常运转,运维非常需要监控系统。在实例的创建速度和销毁速度一样快的容器世界中,灵活配置各类容器的监控项并迅速安装启动监控是非常重要的。

5.2 金融行业

金融服务巨头Northern Trust于2017年6月选择普罗米修斯,不是为了进行应用程序的监视,而是为了更好地了解其某些硬件的运作情况。Northern Trust使用普罗米修斯监控其平台上的750多种微服务。

5.3 汽车行业

Life360是一款用于定位、行车安全和家庭成员之间共享信息的移动应用程序,他们需要给用户提供稳定的定位服务,而原有的监控方案都非常局限,无法监视到所有组件的工作状态。

因此该公司使用普罗米修斯来监视其MySQL多主群集和一个12节点的Cassandra环,该环可容纳约4TB的数据。普罗米修斯在初步测试中表现良好。

在普罗米修斯的有限部署之后,Life360报告了监控方面的巨大进步,并设想在其数据中心基础架构的其他部分中使用它。 

总而言之,普罗米修斯这样的分布式监控系统,在未来的世界中用处可能会越来越大,它或许将会成为监控领域寡头式的存在,希望我们能熟悉这个工具,并在以后的架构和实践中使用它解决系统和应用监控的问题。

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!

给作者打赏,选择打赏金额
¥1¥5¥10¥20¥50¥100¥200 自定义

​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Python编写的超帅数独可视化解题器

数独相信大家都玩过,它被称为“聪明人的游戏”,在很多人眼里:

会玩数独=高智商

为什么?因为数独能够培养观察力,提高反应力: 数独的练习能够锻炼手眼脑的协调性、提高手脑并用的能力,锻炼大脑的思维灵活度,全面提高反应力。

非常适合孩子在成长过程中锻炼大脑,适合成年人在生活中激活思维。

不过当我们遇到不会解的数独怎么办?答案是,用Python算出来!

基于 Pygame-Sudoku-Solver 这个开源项目,可视化解决数独问题变得极其简单。

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。

(可选1) 如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda,它内置了Python和pip.

(可选2) 此外,推荐大家用VSCode编辑器来编写小型Python项目:Python 编程的最好搭档—VSCode 详细指南

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),输入命令安装依赖:

1.在终端输入以下命令下载该开源库

git clone https://github.com/tymscar/Pygame-Sudoku-Solver.git

2.使用cd命令进入该文件夹,并安装依赖:

cd Pygame-Sudoku-Solver
pip install -r requirements.txt

接下来,可以试试运行该项目了:

python solver.py

此时会出现一个空白3*3的九宫格

2.怎么解题

这个开源项目的解题方法如下:

1.输入题目数字 — 你只需要点击空白区域,此时会回显绿色方块,输入数字,如果数字合法则会填入框内,如果不合法则会闪现红色。

2.当你将数独题目里的所有数字填写完毕,单击空格键即可开始运算:

而且,细心的作者还帮大家准备了夜晚模式,单击“d”键可切换到夜晚模式:

3.原理

所有的解题源代码都放在了solver.py文件中,大家可以在里面看到整个解题过程。

作者没有写任何注释,但是代码逻辑思路是清晰的,比如核心判断逻辑,Cell类里的 isValid, 用于判断某个值 (what变量) 放进某个 Cell 里是否合法:

此处,lineV.cells 表示数组中每一列组成的cell;lineH.cells即每一行组成的cell;box.cells即每个子九宫格。他们都有一个共同的特点:其中不能出现重复的值。

因此你会看到如果某个值存在于这些cells当中,isValid直接返回False,表明其不应该出现在这个位置。

如果你的网络较差,git clone拿不到代码,可以在公众号后台回复:数独 下载源代码。

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!

给作者打赏,选择打赏金额
¥1¥5¥10¥20¥50¥100¥200 自定义

​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Python制作国际空间站实时跟踪器

Open Notify是一个开源项目,旨在为NASA的一些出色数据提供简单的编程接口。

open-notify.org 的作者做了一些工作,以获取原始数据并将其转换为与太空和航天器有关的API

本文将通过这个接口,获取得到国际空间站的位置,并实时地绘制到地图上:

感谢cr0sis/Real-time-International-space-station-tracker

为了实现本文的目标,你得先安装ISS_Info:

pip install ISS-Info

下面分步骤讲解整套绘制流程

1.地图初始化

为了实时展示国际空间站的路径,需要使用turtle绘制曲线,因此可以创建一个turtle画布,将背景设为地球:

  
import ISS_Info
import turtle
import time
import json
import urllib.request

screen = turtle.Screen()
screen.setup(720,360)
screen.setworldcoordinates(-180,-90,180,90)
screen.bgpic("map.png")
screen.bgcolor("black")
screen.register_shape("isss.gif")
screen.title("Real time ISS tracker")

iss = turtle.Turtle()
iss.shape("isss.gif")

2.获取空间站的人数

如果能知道空间站上的宇航员人数,我们就能更加准确的跟踪国际空间站。幸运的是open-notify确实提供了这样的接口。

为了获取人数信息,我们必须向:
http://api.open-notify.org/astros.json
请求拿到数据,并将相应的宇航员名字写在左上角:

astronauts = turtle.Turtle()
astronauts.penup()
astronauts.color('black')
astronauts.goto(-178,86)
astronauts.hideturtle()
url = "http://api.open-notify.org/astros.json"
response = urllib.request.urlopen(url)
result = json.loads(response.read())
print("There are currently " + str(result["number"]) + " astronauts in space:")
print("")
astronauts.write("People in space: " + str(result["number"]), font=style)
astronauts.sety(astronauts.ycor() - 5)

people = result["people"]

for p in people:
    print(p["name"] + " on: " + p["craft"])
    astronauts.write(p["name"] + " on: " + p["craft"], font=style)
    astronauts.sety(astronauts.ycor() - 5)

3.绘制空间站位置

为了能够绘制空间站的实时位置,我们需要请求拿到空间站的位置信息。请求的接口是:
http://api.open-notify.org/iss-now.json

不过作者将其封装成了一个函数,我们直接调用 iss_current_loc 即可,循环获取国际空间站位置:

while True:  
    location = ISS_Info.iss_current_loc()
    lat = location['iss_position']['latitude']
    lon = location['iss_position']['longitude']
    print("Position: \n latitude: {}, longitude: {}".format(lat,lon))
    pos = iss.pos() 
    posx = iss.xcor()
    if iss.xcor() >= (179.1):           ### Stop drawing at the right edge of  
        iss.penup()                     ### the screen to avoid a 
        iss.goto(float(lon),float(lat)) ### horizontal wrap round line
        time.sleep(5)
    else:
      iss.goto(float(lon),float(lat))
      iss.pendown()
      time.sleep(5)

我们还可以标出自己目前所处的位置,以查看和国际空间站的距离及空间站经过你上空的时间点(UTC)。

# 深圳
lat = 112.5118928
lon = 23.8534489

prediction = turtle.Turtle()
prediction.penup()
prediction.color('yellow')
prediction.goto(lat, lon)
prediction.dot(5)
prediction.hideturtle()

url = 'http://api.open-notify.org/iss-pass.json?lat=' +str(lat-90) + '&lon=' + str(lon)
response = urllib.request.urlopen(url)
result = json.loads(response.read())

over = result ['response'][1]['risetime']

prediction.write(time.ctime(over), font=style) 

不过这里值得注意的是,iss-pass.json这个接口的纬度计算必须在-90到90之内,因此深圳的纬度需要减去90.

最终效果如下:

在Python实用宝典公众号后台回复“国际空间站”或者“ISS”即可获得本文完整源代码哦。

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!

给作者打赏,选择打赏金额
¥1¥5¥10¥20¥50¥100¥200 自定义

​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Python 导出word所有图片并转化格式

作者:叶庭云

日常工作中,你是否遇到过这样的场景,领导发来一份 Word 文档,要求你将文档中的图片存储到一个文件夹内,并且还要将图片都改成 .jpg 或者 .png,你会怎么办?

你是不是一边内心崩溃,一边开始一张张的 另存为

今天,我就教你两招省时省力的方法,不管文档中有几张甚到几百张图片,你都可以快速保存下来。

一、分析

图片在文档的应用已经是十分普遍的现象了,在Word文档中插入合适的图片无疑会让我们的文档变得更美观。

先来回想一下,我们平常是如何在Word中插入图片的?

  • 在本地电脑中事先存储好需要的图片素材,然后插入到Word中

其实,第二种方法有一个弊端在于图片只存在 Word 中,如果我们需要将它们保存到本地电脑中以供日后使用,最常用的方法是单击鼠标右键,选择 另存为图片,然后选择路径进行保存。

这种方法在只需要处理少数几张图片时还算适用,一旦图片数量增多,处理工作就会变得繁琐且容易出错。

那么,我们怎样可以将这些图片批量保存呢?

二、提取出 Word 文档里的图片

解决方法就是:更改文件格式。直接将 Word 文档的后缀名改成 “.rar” (“.zip”也是可以的)的压缩格式。打开压缩文件,点击【word】-【media】,文档中使用的图片就出现在这里,只需要选中解压出来即可。

用于测试的 Word 文档如下:

操作方法如下:

点击查看,选择详细信息,勾上文件扩展名。

直接将 Word 文档的后缀名改成 “.rar” (“.zip”也是可以的)的压缩格式。

打开压缩文件,点击【word】-【media】,文档中使用的图片就出现在这里,只需要选中解压出来即可。

三、利用 python 批量转换格式

# -*- coding: UTF-8 -*-
"""
@File    :test_01.py
@Author  :叶庭云
@CSDN    :https://yetingyun.blog.csdn.net/
"""
# 导入os模块
import os

# 不存在 jpg图片 这个文件夹  创建
if not os.path.exists('jpg图片'):
    os.mkdir('jpg图片')


path = r'.\jpg图片'
# 列出 media 文件夹下所有图片
files = os.listdir(r'.\media')

for item in files:
    # 拼接出media 文件夹下所有图片路径
    file_1 = '.\media' + '/' + item
    # 读取图片数据
    with open(file_1, 'rb') as f:
        con = f.read()
    # 重新写入  以 .jpg 格式 并保存到jog图片文件夹
    file_name = path + '/' + item.split('.')[0] + '.jpg'
    with open(file_name, 'wb') as f:
        f.write(con)

运行效果如下:

程序运行,嗖的一下,图片格式都转换成了 .jpg 并保存到新的文件夹里。

作者:叶庭云

CSDN:https://blog.csdn.net/fyfugoyfa

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!

给作者打赏,选择打赏金额
¥1¥5¥10¥20¥50¥100¥200 自定义

​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Django Celery 异步与定时任务实战教程

Django与Celery是基于Python进行Web后端开发的核心搭配,在运营开发(即面向企业内部)的场景中非常常见。

下面是基于Django的Celery异步任务和定时任务的实战教程,大家觉得有用的话点个赞/在看吧!

1.配置Django Celery

配置celery主要有几点:

1. 在settings.py的同级目录下,创建celery.py文件(名字自己随意取),这个文件主要是用来生成celery的实例app.

from __future__ import absolute_import, unicode_literals
import os
from celery import Celery
from django.conf import settings

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'NBAsite.settings')

app = Celery('NBAsite',broker='redis://localhost:6379/0',backend='redis://localhost')
app.config_from_object('django.conf:settings',namespace='CELERY')
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)

我们将 celery 实例的 broker 和 backend 都设为了redis.

其中 broker 的意思是“经纪人”,像股票经纪人一样,是用于促成“交易”的,Celery中它的职责就是给 worker 推送任务。

而backend的职责是存放执行信息和结果,这些数据需要被持久化存于数据库。但为了简化问题,我们将其与broker一样放置于redis当中。

2. 需要你在自己已经创建的app(不是celery的app,而是django项目的app)目录下面,创建task.py文件(这个文件名只能是这个)

因为Celery会统一从每个app下面的tasks里面监听任务。

3. 编写tasks.py的任务

看一下tasks内部的任务如何写:

from __future__ import absolute_import, unicode_literals
from NBAsite.celery import app
from celery import shared_task
import time

@shared_task
def waste_time():
    time.sleep(3)
    return "Run function 'waste_time' finished."

任务的目标是延迟3秒后,返回一个语句。

4. init.py中的设置

这个是非常关键的一点,如何让django在启动的时候,也把celery给启动了呢?
答案是在项目的init文件内,导入celery的app

from __future__ import absolute_import, unicode_literals
import pymysql

from .celery import app as celery_app

pymysql.install_as_MySQLdb()
__all__ = ('celery_app',)

2.Django 其他配置

为了能够触发该异步任务,我们接下来配置一些常规文件,views和url,首先是views函数:

from .tasks import waste_time

def test_c(request):
    result = waste_time.delay().get()
    return JsonResponse({'status':'successful'})

然后是url:

path('test_c', test_c, name='test_c'),

3.进行测试

首先,运行django项目

python manage.py runserver

这样,django项目和celery的app就被一起启动了,但是这个时候是无法执行这个task的,因为worker没有被启动,我们可以试一下:

访问http://127.0.0.1:8000/stats/test_c 会得到以下报错:

正确的姿势是怎么样的?需要先激活worker,然后再访问API:

celery -A NBAsite worker -l info

从上图下方的log信息里可以看到,在延迟了3秒后,任务启动并返回字符串,而在页面上,也可以看到成功返回。

需要注意的是,如果你修改了tasks的内容,是需要重启celery才能生效的,最简单的方法就是重启django项目。

这样,我们就完成了简单的异步任务的配置和使用。

4.定时任务配置

在异步任务中,我们只用到了worker,而在定时任务中,还要用到celery的beat调度器。

首先来看下如何配置定时任务,或者说如何配置这个调度器。

还是在celery.py里面进行配置:

from __future__ import absolute_import, unicode_literals
import os
from celery import Celery
from celery.schedules import crontab
from django.conf import settings

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'NBAsite.settings')

app = Celery('NBAsite',broker = 'redis://localhost:6379/0',backend='redis://localhost')

app.config_from_object('django.conf:settings',namespace='CELERY')

app.conf.beat_schedule ={
        'autosc':{                           
            'task':'stats.tasks.auto_sc',    
            'schedule':crontab(hour=20,minute=47),   
        },
}
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)

重点是增加了app.conf.beat_schedule这个定时任务配置,指定了 stats 文件夹下 tasks.py 中的auto_sc函数,定时于20:47分执行。

5.具体任务页面tasks

增加一个对应要做定时任务的task

@shared_task
def auto_sc():
    print ('sc test?')
    return 'halo'

6.运行命令和结果

命令的话可以将激活worker和激活beat合并在一起,如下:

celery -A NBAsite worker -B -l info

不过,windows不被允许这么使用,因此在windows环境下,你需要同时打开worker和beater:

celery -A NBAsite worker -l info
celery -A NBAsite beat -l info

看上图下方的log可知定时任务被成功执行。

参考资料:
https://www.jianshu.com/p/173070bcdfaf
https://www.jianshu.com/p/ee32074a10de

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!

给作者打赏,选择打赏金额
¥1¥5¥10¥20¥50¥100¥200 自定义

​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Python 用5行代码学机器学习—线性回归

之前Python实用宝典讲过许多关于机器学习的文章,比如:

Python 短文本自动识别个体是否有自杀倾向

[准确率:98%] Python 改进朴素贝叶斯自动分类食品安全新闻 实战教程

准确率94%!Python 机器学习识别微博或推特机器人

Python 机器学习预测泰坦尼克号存活概率

等等…

但是这些文章所使用的模型,读者在第一次阅读的时候可能完全不了解或不会使用。

为了解决这样的问题,我准备使用scikit-learn,给大家介绍一些模型的基础知识,今天就来讲讲线性回归模型。

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上噢,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),准备开始输入命令安装依赖。

当然,我更推荐大家用VSCode编辑器,把本文代码Copy下来,在编辑器下方的终端运行命令安装依赖模块,多舒服的一件事啊:Python 编程的最好搭档—VSCode 详细指南。

在终端输入以下命令安装我们所需要的依赖模块:

pip install scikit-learn

看到 Successfully installed xxx 则说明安装成功。

2.简单的训练集

冬天快到了,深圳这几天已经准备开始入冬了。

从生活入手,外界温度对是否穿外套的影响是具有线性关系的:

外界温度是否穿外套
30度
25度
20度
15度
10度

现在,考虑这样的一个问题:如果深圳的温度是12度,我们应不应该穿外套?

这个问题很简单,上述简单的训练集中,我们甚至不需要机器学习就能轻易地得到答案:应该。但如果训练集变得稍显复杂一些呢:

你能看出其中x1, x2, x3和y之间的规律吗?

比较难,但是如果你有足够的数据(比如100个),机器学习能够迅速解决这个问题。

为了方便展示机器学习的威力,我们在这里生产100个这样的训练集(公式为: y=x1 + 2*x2 + 3*x3):

from random import randint
TRAIN_SET_LIMIT = 1000
TRAIN_SET_COUNT = 100

TRAIN_INPUT = list()
TRAIN_OUTPUT = list()
for i in range(TRAIN_SET_COUNT):
    a = randint(0, TRAIN_SET_LIMIT)
    b = randint(0, TRAIN_SET_LIMIT)
    c = randint(0, TRAIN_SET_LIMIT)
    op = a + (2*b) + (3*c)
    TRAIN_INPUT.append([a, b, c])
    TRAIN_OUTPUT.append(op)

然后让线性回归模型使用该训练集(Training Set)进行训练(fit),然后再给定三个参数(Test Data),进行预测(predict),让它得到y值(Prediction),如下图所示。

3.训练和测试

为什么我使用sklearn?因为它真的真的很方便。像这样的训练行为,你只需要3行代码就能搞定:

from sklearn.linear_model import LinearRegression

predictor = LinearRegression(n_jobs=-1)
predictor.fit(X=TRAIN_INPUT, y=TRAIN_OUTPUT)

需要注意线性回归模型(LinearRegression)的参数:

n_jobs:默认为1,表示使用CPU的个数。当-1时,代表使用全部CPU

predictor.fit 即训练模型,X是我们在生成训练集时的TRAIN_INPUT,Y即TRAIN_OUTPUT.

训练完就可以立即进行测试了,调用predict函数即可:

X_TEST = [[10, 20, 30]]
outcome = predictor.predict(X=X_TEST)
coefficients = predictor.coef_

print('Outcome : {}\nCoefficients : {}'.format(outcome, coefficients))

这里的 coefficients 是指系数,即x1, x2, x3.

得到的结果如下:

Outcome : [ 140.]
Coefficients : [ 1. 2. 3.]

验证一下:10 + 20*2 + 30*3 = 140 完全正确。

如何,机器学习模型,用起来其实真的没你想象中的那么难,大部分人很可能只是卡在了安装 scikit-learn 的路上…

顺便给大家留个小练习,将下列欧式距离,使用线性回归模型进行表示。

解决思路和本文的方案其实是类似的,只不过需要变通一下。

解决出来的同学可在后台回复:加群,将代码发给我验证,领取一份小红包并进入Python实用宝典的高质量学习交流群哦。

我们的文章到此就结束啦,如果你喜欢今天的Python 实战教程,请持续关注Python实用宝典。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 (pythondict.com)
不只是一个宝典
欢迎关注公众号:Python实用宝典