机器学习之step by step实战及知识积累笔记
数据工作者工作时间划分
据crowdflower数据科学研究报告,数据科学工作者的时间分配主要在以下几个领域:
首先是数据收集要占20%左右的时间和精力,接着就是数据清洗和再组织需要占用60%的时间。也就是说数据科学家80%的精力都花在了数据收集和预处理,从而生成能够用于训练模型的训练集。真正的算法优化和训练只占4%左右,另外10%左右用于特征提取,数据再造。
正确的特征集及足够的数据量决定了机器学习效果的上限,算法的优化可以无限逼近这个上限
机器学习的一般流程
获取kaggle titanic数据集
以前通过requests以及session就能够先登录kaggle,然后直接get到相应的dataset,但是似乎kaggle也在做相应的转型,现在只能通过kaggle的命令行操作:
kaggle.exe datasets download -d rashigoel/titanic-machine-learning-from-disaster #Downloading titanic-machine-learning-from-disaster.zip to C:\Users\zhenghuz\.kaggle\datasets\rashigoel\titanic-machine-learning-from-disaster
%|▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒| .9k/.9k [:<:, 105kB/s]
cleaning and organizing
这个过程具体有哪些工作要做:
经过数据收集这个过程拿到raw data后,我们首先需要通过常规的统计分析及数据可视化工具手段来熟悉和学习这些数据,该过程我们称之为EDA(exploratory Data Analysis),比如这些数据中有没有缺失的字段,有没有明显背离常识范围的数据?这个阶段我们发现数据问题,随后我们要经过一个所谓"数据清洗"的过程(data munging)来解决这些问题。比如对于缺失部分字段的数据可能需要使用某种平均值的算法来做填充,或者彻底清除。对于明显超范的数据做抛弃处理等。。到这里,数据基本上是可以用的并且“干净”可靠的了,但是在喂给学习算法之前,往往我们要做一个名为“特征工程”(feature engineering)的工程过程,主要是从已有的数据attribute属性中,我们找出那些真正对结果有重要影响的属性作为"feature特征",或者我们可能会组合创造出新的feature(特征)用于算法学习。同时我们可能要通过相关性探究发现那些重复的特征,剔除冗余信息,只保留独立的特征数据。
在这个过程中,我们可能会用到一些交叉数据特征探究的高级数据可视方法(advanced visulization),来辅助对数据的认识和特征构造及提取。
numpy and pandas
numpy是python数据科学的基础库,其提供了非常强大高效的n维数组。pandas基于numpy,提供了dataframe,series等数据结构,非常方便地以类似表格的形式来做数据检索和处理。pandas也提供基于matplotlib的图形库,方便数据学习和特征提取。pandas的dataframe每一行都可以看作是一个observation,每一列都可以看作是一个feature
Exploratory Data Analysis(EDA)
EDA阶段我们主要探索数据集的基础结构(basic structure), 综合统计(summary statistics),数据分布特性(distribution),分组特征(grouping),交叉特征和pivot
基础结构(basic structure)
- 有多少行数据(observation)?
- 每个observation有多少个feature?
- feature对应的数据类型
- 每一行大概长什么样子?(可能要通过tail,head命令)
首先读取数据
import pandas as pd
import numpy as np
import os
raw_data_path = os.path.join(os.path.curdir,'data','raw')
train_file_path = os.path.join(raw_data_path,'train.csv')
test_file_path = os.path.join(raw_data_path,'test.csv')
# read the data with pandas into datafram
# train_df,test_df: pandas.core.frame.DataFrame
train_df = pd.read_csv(train_file_path,index_col='PassengerId')
test_df = pd.read_csv(test_file_path,index_col='PassengerId')
随后使用基础pandas数据检查方法
train_df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 891 entries, 1 to 891
Data columns (total 11 columns):
Survived 891 non-null int64
Pclass 891 non-null int64
Name 891 non-null object
Sex 891 non-null object
Age 714 non-null float64
SibSp 891 non-null int64
Parch 891 non-null int64
Ticket 891 non-null object
Fare 891 non-null float64
Cabin 204 non-null object
Embarked 889 non-null object
dtypes: float64(2), int64(4), object(5)
memory usage: 83.5+ KB
综合统计(summary statistics)
每个observation行数据的列可以分为两类:数值类和类别类,我们可以分别从以下几个方面来关注:
数值类别
中值度量:(mean, meadian)
mean:简单对所有value的算术平均,可以说是数据的中点。
median是针对mean均值的改进,如果一个数据集中有几个非常巨大的异常数据,这时我们简单使用均值则不能反映数据的真实特征,但是如果我们先把这个数据集做一下排序,随后取这个排序序列的中间的值,则能有效剔除那些巨大或者巨小的数据带来的均值问题。也就是说如果变量的分布是skiew偏离值比较大,则非常适合使用median值来描述该variable
mean和median指标非常适合描述quantitative型变量
离散度量(dispersion):(range, percentile, variance, standard deviation)
离散(分散)的度量指标描述数据相对中心点的分散情况。
类似的range本身也会受到巨大或者巨小异常数据的干扰,我们可以使用percentile这个来度量。
percentitle: x percentile is y意味着:x%的值都小于y这个数值。比如 50 percentile是10可以这样解读:50%的值都小于10这个数据, 75 percentile 是12则解读为75%的值都小于12
反映percentitle指标常用Box-Whisker Plot来形象表达:
方差(variance)度量每个数值对均值的偏离程度,方差越小意味着数据分散性越小
方差这个指标同样会受巨大或者巨小数值的影响,同时由于方差是偏差的平方,其单位将失去比较的意义,因此更多时候我们使用标准偏差(standard deviation)这个指标,因为该指标可以清晰的看到数据的分散情况
# dispersion measures
print('Min fare : {0}'.format(df.Fare.min())) # minimum
print('Max fare : {0}'.format(df.Fare.max())) # maximum
print('Fare range : {0}'.format(df.Fare.max() - df.Fare.min())) # range
print('25 percentile : {0}'.format(df.Fare.quantile(.25))) # 25 percentile
print('50 percentile : {0}'.format(df.Fare.quantile(.5))) # 50 percentile
print('75 percentile : {0}'.format(df.Fare.quantile(.75))) # 75 percentile
print('Variance fare : {0}'.format(df.Fare.var())) # variance
print('Standard deviation fare : {0}'.format(df.Fare.std())) # standard deviation
# 注意:如果数据集中有空值,则pandas对应的统计数据就会出现Nan值,这时我们就必须对这些数据处理
df.Fare.plot(kind='box')
df.describe(include='all')
类别数据
总类别数(total count),唯一类别数(unique count),类别总数及其占比(category counts and proportion),每类别统计(per category stastics)。
mode: 发生频率最高的那个value,非常适合categorical特征的度量
数据分布特性(distribution)
单变量的分布图(univariate)可以使用直方图(Histogram), Kernel Density Estimation(KDE) plot来完美呈现
直方图histogram概念模型解释:
kde实际上将直方图中的频率换算成了对应的概率。相应地,后面我们也会引入概率分布,比如正态分布,skewness等概念
对于标准正态分布,没有任何skewness,其mean均值和median的值相等,大于均值和小于均值的数值均等分布。
如果median<mean则称为左偏分布,说明一定有巨大的值参杂其中或者较大的值占比比较高;
如果median>mean则称为右偏分布,说明一定有巨小的值参杂其中或者较小的值占比比较高
两个变量的(bivariate)分布图可以使用散点图Scatter plot, scatter plot非常适合描述两个feture之间的关联关系.例如你可能设想Age这个特征和Height这个特征应该具有一定的关系(pattern),我们就可以非常方便地通过这个散点图一眼看出他们的关系。
http://seaborn.pydata.org/tutorial/categorical.html#categorical-tutorial
变量之间的相关性:pearson's R
在特征工程中,如果两个特征之间具有线性相关性,那么不应该都应用到机器学习中,而应该剔除其中一个。研究特征之间线性相关性的指标为pearson's R.该指标可以指示是正相关,还是负相关以及相关的程度。其值在-1到1之间,0为不相关,1为最大正相关,-1为最大负相关。其计算方法:
其中
SDx和SDy是x和y两个feature的标准差
pearson R计算示意图:
数据的grouping
数据的grouping是指根据选择的feature,对row进行分组聚合,分类组合后,我们可以对每个分组应用不同的数字统计计算,比如mean,median,count等
交叉表格(crosstabs)
crosstab非常适合于探究category类型的数据,其按照crosstab的feature罗列出每个交叉类别对应的observation次数
Pivot
pivot table是对crosstable的自然延伸,和crosstable不同的是,他使用某个feature的值来代替crosstable中表示发生次数的数值。
df.pivot_table(index='Sex',columns='Pclass',values='Age',aggfunc='mean')
Data Munging
正如前面所述,在EDA阶段,我们只是对我们的数据集做了初步的探索,发现可能存在的问题,而在这个data munging阶段,我们则需要解决EDA阶段发现的数据问题。
- 缺失的数据项处理(value missing);
- 对巨大值巨小值的处理(extreme value/outliers);
- 错误数据处理(erroneous values)
value missing
产生的原因:数字确实未知,数据录入错误,设备错误(特别是物联网传感器数据产生了错误)
解决方案:
- 删除
- 数据补偿
- 使用mean均值(适合于数值型feature)
- 使用median均值(适合于数值型feature)
- 使用最高频的category值来填充(适合于category类型feature)
- 使用左邻或者右邻数据来填充(适合于category类型feature,并且数据是有序排列的)
- 使用某种预测模型来填充(比如线性模型)
处理过程:先使用df.info()获取missing的是哪些字段,使用df[df.xxx.isnull()]过滤列出那些observation,结合缺失数据已有的其他信息字段,来推断我们应该用什么统计信息来填写
df[df.Fare.isnull()]
medianFare_in_embarkedS_pclass3 = df[(df.Pclass==3)&(df.Embarked=='S')].Fare.median()
df.Fare.fillna(medianFare_in_embarkedS_pclass3, inplace=True)
df[df.Age.notnull()].boxplot('Age',['Pclass','Sex']) # 通过boxplot罗列出Age和Pclass, Sex组合下各种分类的Age分布情况,如果发现分布差异巨大,则
# 非常适合选择这些特征作为辅助项目。相反,如果发现groupby分布差异不大,则最好不要使用该groupby的median来做填充!!
从上面的图中,我们可以看到Age的分布针对Sex和Pclass来groupby的话,其值具有明显的差异性,因此非常适合我们就使用Pclass+Sex对应的Age median值来做缺值的填充
outlier值的处理
outlier是指明显远远大于或者远远小于正常均值的值,这些outlier在EDA阶段会产生Biased analysis,比如mean, ,range, deviation都受到巨大值的巨大影响。而如果这些outlier值用于训练模型并产生预测,则会产生Biased model。
但是outlier也往往会携带非常有意义的信息,给我们以重要的启示,因此不能一概而论,outlier存在于数据集就是不好的。
outlier值的检测:
单变量的hitogram
单变量的Boxplot
两个变量组合时可以使用scatter plot
outlier值处理:
- 删除
- 变换transformation,比如log
- 分段binning
- imputation: 替换为更有意义的值(需要小心,因为outlier值可能携带重要信息,你一旦替换可能丢失)
Feature Engineering(Domain knowledge + Technical Expertise):
特征工程(feature engineering)是而对原始数据进行变换以便更能表征特征pattern(better representative)从而能够创建更好预测模型的流程
- 变换(transformation)
- 再生新feature(依赖于领域专家知识和经验)
- 类别型feature的编码:
类别型feature的编码
由于机器学习只能使用数值型变量,因此对于category类型的特征feature我们必须编码成对应的数字型数据,否则无法进入机器学习。一般地,常见的编码方法有:
binary encoding
非常适合于只有两个类别值的category特征编码,比如性别:男和女,中国人和外国人。。这种直接用is_male, is_chinese其值编码为1,0
label encoding
非常适合于有多个类别值,同时其类别有序列意义的category feature编码,比如收入:底,中,高,成绩:不及格,及格,良好,优秀,满分。这种类型的数据本身直接用0,1,2,3,来编码其类别值是有意义的。
one-hot encoding
如果待编码的类别数据本身没有序列意义,则最安全的编码方式就是这种one-hot编码方式了,其根据cate类别值分别创建is_catea,is_cateb,is_catec等。
advanced data visulization
matplotlib相比于pandas可视化有更强大的功能,比如支持subplot,一个visulization同时展示多张图片
机器学习模型训练
有了数据后,我们就可以根据问题类型预先设定一个模型,比如logistic regression, SVM,神经网络,将这些数据应用到学习算法中,找到能够较好拟合这些数据的模型。
这时我们就有了一个trained model,再将test data输入到这个已训练模型中,根据预测值和实际值的差异得出评价指标(比如逻辑回归的正确率),如果不满意,则可以通过调整对应模型的超参数继续重新训练得到一个较好结果的模型。或者我们重新选择模型训练得到一个完全重构的已训练模型,直到对模型预测指标满意为止。
一般的,我们在应用数据创建训练模型之前我们可以先设定一个基础模型,该模型的输出对于分类器来说,我们就选择那个最高出现频率的类别,比如对于泰坦尼克号存活率如果大部分都是不能幸存,比如60%,那么我们就将基础模型设定输出为不能幸存。那么如果我们新训练出来的模型准确率小于 60%,那么就表明我们的模型是完全失败的,预测模型是没有任何意义的!!!
还有一点需要注意的是对于逻辑回归,我们要检视一下0和1输出的占比,如果严重失衡的话,我们有必要处理(im-balanced classifications)
https://www.analyticsvidhya.com/blog/2016/03/practical-guide-deal-imbalanced-classification-problems/
import pandas as pd
import numpy as np
import os
processed_data_path = os.path.join(os.path.curdir,'data','processed')
train_file_path = os.path.join(processed_data_path,'train.csv')
test_file_path = os.path.join(processed_data_path,'test.csv')
train_df = pd.read_csv(train_file_path,index_col='PassengerId')
test_df = pd.read_csv(test_file_path,index_col='PassengerId')
X = train_df.loc[:, 'Age':].as_matrix().astype('float')
y = train_df['Survived'].ravel()
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=0)
下面我们创建一个dummy model用作baseline model:
import sklearn
from sklearn.dummy import DummyClassifier
#create dummy model
model_dummy = DummyClassifier(strategy='most_frequent',random_state=0)
#train dummy model
model_dummy.fit(X_train,y_train)
model_dummy.get_params()
print('score for dummy classifier is: {0:.2f}'.format(model_dummy.score(X_test,y_test))) # 0.61
logistic model
逻辑回归模型可以简单看作是线性回归模型应用在sigmoid函数后实现非线性后形成的model,输出值为概率值0到1
我们来看看Logistic model对应的代码
from sklearn.linear_model import LogisticRegression
# create model
model_lr_1 = LogisticRegression(random_state=0)
# train the model
model_lr_1.fit(X_train,y_train)
# evaluate model
print('score for logistic regression -version 1: {0:.2f}'.format(model_lr_1.score(X_test,y_test))) # 0.83
model_lr_1.intercept_ # array([1.10324257]) 截距
model_lr_1.coef_ # 对应每个特征的系数
'''
array([[-0.02840734, 0.00455631, -0.50017007, 0.61922839, -0.81414743,
0.12823264, -0.17253859, -0.39355489, 0.52215008, 1.09939125,
0.40346551, -0.18369316, -0.30021028, 0.37253571, 0.73070686,
0.16297325, 0.2474635 , 0.27998253, 0.41282329, 0.28258585,
1.21850069, 0.56334182, -1.44612508, 1.07146232, -0.11345497,
-0.47306807, 0.49202885, 0.46214499, 0.14906873, 0.96558544,
0.48281793, -0.3451608 ]])
'''
分类器效果评估(precision, recall,accuracy)
Precision:查准率(预测为1中,真值为1的比例(概率)) $Precision = \frac{TP}{TP+FP} = P(y_i=1|\widehat{y_i}=1)$
Recall:查全率(真值为1中,预测值也为1的比例(概率))$Recall = \frac{TP}{TP+FN} = P(\widehat{y_i}=1|y_i=1)$
Precision和Recall都是针对Positive事件的,往往是我们感兴趣的事件,相对地,我们也可以取Negative事件的指标。
总准确率: (所有预测正确的比例(含1,0))$accuracy = \frac{TP+TN}{TP+TN+FP+FN} = P(\widehat{y_i}==y_i)$
模型调优(model tuning)
在通过sklearn logistic regression model训练后我们得到第一版trained model,效果还不错达到83%的准确率。下面我们看看还有哪些可以继续优化的地方来不断优化模型,有一个更好的呈现效果。
underfitting vs overfitting
underfitting(欠拟合):模型对训练集的拟合不足,也就是说即使是训练集,模型本身都不能准确预测
overfitting(过拟合):模型过于复杂,完全是基于training data的拟合,无法泛化
regularization
我们知道模型如果过于复杂容易产生过拟合,如果太简单容易产生欠拟合,针对逻辑回归模型我们有一些参数来调整模型的复杂度等参数。
比如C参数表征复杂度,数字越大越复杂,penalty是正规惩罚项。除了逻辑回归Model,越复杂的模型类别,其超参数越多。数据科学家很重要的一个
工作是寻求一组最优化的超参数组合,使得model预测效果最佳。
hyperparameter tuning
超参数优化的基本思路是将参数组合做成表格,分别针对这些不同的组合来训练模型给出性能指标,后面我们选择一个最佳的指标来。
cross validation
样例代码:
model_lr_base = LogisticRegression(random_state=0)
from sklearn.model_selection import GridSearchCV
hparameters = {'C':[1.0,10.0,50.0,100.0,1000.0],'penalty':['l1','l2']}
clf = GridSearchCV(model_lr_base,param_grid=hparameters,cv=3) # cv表示3 K FOLD CROSS VALIDATION
clf.fit(X_train,y_train)
clf.best_params_ # {'C': 1.0, 'penalty': 'l1'}
Feature normalization and Feature Standadization(特征正规化)
如果输入特征数据的range变化很大,很有可能对训练出来的模型产生偏离效果,我们最好在训练模型之前将输入数据正规化处理,使得所有feature都在0到1的范围内,并且具有正态分布的特征(期望为0,方差为1)
更换其他类型的逻辑回归
几个概念:centered, standardized,normalized:
model_lr_base = LogisticRegression(random_state=0)
hparameters = {'C':[1.0,10.0,50.0,100.0,1000.0],'penalty':['l1','l2']}
clf = GridSearchCV(model_lr_base,param_grid=hparameters,cv=3) # cv表示3 K FOLD CROSS VALIDATION
clf.fit(X_train_scaled,y_train)
clf.best_params_
实验结果表明正规化参数后并未对预测结果产生明显的改进,反而有点下降
模型持久化和API
model api要實現的功能是:接收raw data,并且处理他,做出预测,并且返回json
Feature normalization
机器学习实战中遇到的问题解决方案
Traceback (most recent call last):
File "C:/Users/Administrator/devenvironment/Code/intro_ds/ch04-linear/simple_example/linear_stat.py", line 14, in <module>
import statsmodels.api as sm
File "C:\Users\Administrator\Anaconda3\lib\site-packages\statsmodels\api.py", line 5, in <module>
from . import regression
File "C:\Users\Administrator\Anaconda3\lib\site-packages\statsmodels\regression\__init__.py", line 1, in <module>
from .linear_model import yule_walker
File "C:\Users\Administrator\Anaconda3\lib\site-packages\statsmodels\regression\linear_model.py", line 43, in <module>
from scipy.stats.stats import ss
ImportError: cannot import name 'ss'
conda upgrade statsmodels/pip install statsmodels --upgrade
ImportError: No module named 'tensorflow'
解決方案:
conda install tensorflow
SKLearn算法库选择指南
https://scikit-learn.org/stable/tutorial/machine_learning_map/index.html
机器学习之step by step实战及知识积累笔记的更多相关文章
- deep learning自学知识积累笔记
推荐系统的演变过程 协同过滤(英雄所见略同)思想为类似喜好的人的选择必然也类似.比如小学男生普遍喜欢打手游,中年大叔普遍喜欢射雕英雄传 随后有了SVD奇异值分解,但是SVD要求不能太稀疏,因此有了隐语 ...
- Step By Step(Lua基础知识)
Step By Step(Lua基础知识) 一.基础知识: 1. 第一个程序和函数: 在目前这个学习阶段,运行Lua程序最好的方式就是通过Lua自带的解释器程序,如: /> l ...
- WPF Step By Step 系列 - 开篇 ·
WPF Step By Step 系列 - 开篇 公司最近要去我去整理出一个完整的WPF培训的教程,我刚好将自己学习WPF的过程和经验总结整理成笔记的方式来讲述,这里就不按照书上面的东西来说了,书本上 ...
- Step by Step 真正从零开始,TensorFlow详细安装入门图文教程!帮你完成那个最难的从0到1
摘要: Step by Step 真正从零开始,TensorFlow详细安装入门图文教程!帮你完成那个最难的从0到1 安装遇到问题请文末留言. 悦动智能公众号:aibbtcom AI这个概念好像突然就 ...
- WPF Step By Step 完整布局介绍
WPF Step By Step 完整布局介绍 回顾 上一篇,我们介绍了基本控件及控件的重要属性和用法,我们本篇详细介绍WPF中的几种布局容器及每种布局容器的使用场景,当 然这些都是本人在实际项目中的 ...
- WPF Step By Step 控件介绍
WPF Step By Step 控件介绍 回顾 上一篇,我们主要讨论了WPF的几个重点的基本知识的介绍,本篇,我们将会简单的介绍几个基本控件的简单用法,本文会举几个项目中的具体的例子,结合这些 例子 ...
- 【学习笔记】Baby Step Giant Step算法及其扩展
1. 引入 Baby Step Giant Step算法(简称BSGS),用于求解形如\(a^x\equiv b\pmod p\)(\(a,b,p\in \mathbb{N}\))的同余方程,即著名的 ...
- 稀疏表示step by step(转)
原文地址:稀疏表示step by step(转)作者:野火春风 稀疏表示step by step(1) 声明:本人属于绝对的新手,刚刚接触“稀疏表示”这个领域.之所以写下以下的若干个连载,是鼓 ...
- Step By Step(Lua-C API简介)
Step By Step(Lua-C API简介) Lua是一种嵌入式脚本语言,即Lua不是可以单独运行的程序,在实际应用中,主要存在两种应用形式.第一种形式是,C/C++作为主程序,调用Lua代码, ...
随机推荐
- Ubuntu系统下开发人员常用工具、命令和技巧
在新的Ubuntu系统安装完成后,开发人员一般需要下载.安装一些必备的工具,并进行一系列的环境配置等操作,本文对此做出一些总结,方便今后新开发环境的初始化. 一.文件常用安装目录和命令 一般的deb包 ...
- ASP.NET Core 中的对象映射之 AutoMapper
目录 AutoMapper 简介 AutoMapper 使用 初始化 Profile设置 扁平化映射 集合映射 投影 条件映射 值转换 设置转换前后行为 配置验证及设置 反向映射 自定义转换器 自定义 ...
- Kafka消息重新发送
Kafka消息重新发送 1. 使用kafka消息队列做消息的发布.订阅,如果consumer端消费出问题,导致数据并没有消费,此时不需要担心,数据并不会立刻丢失,kafka会把数据在服务器的磁盘 ...
- JavaScript函数——预编译
四部曲 创建AO对象 找形参和变量声明,将变量和形参名作为AO属性名,值为undefined. 将实参值和形参值统一 在函数体内找函数声明,值赋予函数体. 权重按顺序依次增加.以下例子即可体现上述规则 ...
- awk统计文件中某关键词出现次数
1.统计文件test.txt中第2列不同值出现的次数 awk '{sum[$2]+=1}END{for(i in sum)print i"\t"sum[i]}' test.txt ...
- UNIX高手应该保持的习惯
UNIX 高手的 10 个习惯 克服不良的 UNIX 使用模式 采用 10 个能够提高您的 UNIX® 命令行效率的好习惯——并在此过程中摆脱不良的使用模式.本文循序渐进地指导您学习几项用于命令行操作 ...
- maven-shade-plugin 打包出错
一般maven-shade-plugin 打包出错的原因都是因为jar包出错,一般使用mvn package -X 即可找出对应错误的jar包删除即可.我自己遇到的是打开自己打包完的jar包出错,整的 ...
- spring boot入门笔记 (二) - application.properties配置文件
项目最重要的一个东西,用来定义整个项目的一些东西(端口.访问项目的名称.数据源.日志.集成mybatis等框架.静态资源目录.thymeleaf.热部署等),很重要很重要的. #整个项目的端口号,默认 ...
- sql按月统计数量和按月累加统计数量
1.简单的,按月统计数量 SELECT CREATE_DATE, DATE_FORMAT(CREATE_DATE, '%Y-%m') AS month , COUNT(*) AS sum FROM p ...
- xamarin.Android 选择本地图片、拍摄图片、剪裁图片
[Activity(Theme = "@style/MyStyleBottom")] public class SelectPicPopupWindow : Activity, I ...