Author Archives: jkl

使用pandas+openpyxl生成多子表excel

虽然python+pandas自身有强大的数据分析和展现能力,可实际工作中,不是每个人都会coding的,所以,有时候把粗加工的数据,以excel的形式提供数据,会更灵活、更适合大部分,这里介绍一种可以生成多子表excel的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import pandas as pd
from openpyxl import Workbook
from openpyxl.reader.excel import load_workbook
 
#数据暂存容器
xlsDatas = {}
#保存路径
savePath = '.../test-excel.xlsx'
#获取数据
df1 = pd.DataFrame(
    {
        '日期': ['2021-01-01', '2021-01-02', '2021-01-03', '2021-01-04'], 
        '订单数': [1234, 1321, 3456, 6543],
        '用户数': [234, 787, 987, 545]},
    columns=['日期', '订单数', '用户数']
)
xlsDatas['概况'] = df1  #子表名-子表数据
df2 = pd.DataFrame(
    [
        [56335, '兰州拉面', 15.0, '2021-01-01 12:12:12'], 
        [45135, '山西削面', 13.0, '2021-01-01 12:16:12'], 
        [16335, '兰州拉面', 15.0, '2021-01-01 12:32:12']
    ],
    index=list('123'),
    columns=['用户id', '产品名称', '消费金额', '消费日期']
)
xlsDatas['订单列表'] = df2
#使用Workbook保存数据
book0 = Workbook()
book0.save(savePath) #先保存一个空excel文件,然后往里塞子表,实际会有一个空的Sheet子表
book = load_workbook(savePath)
writer = pd.ExcelWriter(savePath, engine='openpyxl') #选择openpyxl引擎
writer.book = book
for k in xlsDatas :
    xlsDatas[k].to_excel(writer, sheet_name=k)
writer.save()

————
over
转载请注明出处:http://www.jiangkl.com/2021/08/pandas_openpyxl_excel

python数字数组的join操作

从其他弱类型语言转到python,最大的感受就是,时不时会有这种感慨:
“卧槽,这样也行”
“卧槽,这也不行”
比如,数组的差值操作,可以直接这么取:
listDiff = list1 – list2
再比如,刚刚碰到的这个,数组的join操作
uIdArr是数字数组,要拼到sql里做查询
“,”.join(uIdArr)
这样?
会报错。。。很正常的一个操作,居然过不去。。。
需要吧userIdArr变成字符串数组
~~好吧
那么,开个for循环?
for当日可以,但是对于python这种追求简单的语言,必然有更简洁的方式:
“,”.join([str[uid] for uid in uIdArr])
——-
over

apply与lambda简单搭配,一行代码实现按条件补齐数据

需求:订单列表里的点位信息,只有点位编号,没有点位场景类型,需要补齐这个数据,以便实现按场景分析订单数据

1
2
3
4
5
  orderDf = pd.read_csv('path...')  #订单数据
  nodePlaces = ... #查库,返回 点位编号 - 场景类型 字典数据
  orderDf['place'] = orderDf.apply(lambda x: nodePlaces[x.nodeId] if x.nodeId in nodePlaces else 0, axis = 1) //#补齐场景类型字段place
  print(orderDf.colums)  #查库补齐效果
  print(orderDf.tail(5))

实际测试,orderDf七千万行,nodePlaces五万个点位,添加place这一行,执行时间约为1分钟
over
—–
转载请注明出处:http://www.jiangkl.com/2021/07/pandas_apply_lambda
—–
两周后补充:
这个需求,还有一个效率更高的方法,就是使用pd.merge()
已上面的例子继续折腾:

1
2
3
4
5
6
  orderDf = pd.read_csv('path...')  #订单数据
  nodePlaces = ... #查库,返回 点位编号 - 场景类型 字典数据
  nodeDf = pd.DataFrame(list(nodePlaces()), columns=['nodeId', 'place']) #转成datafram
  orderDf = pd.merge(orderDf, nodeDf, on='nodeId', how='left') //#补齐场景类型字段place
  print(orderDf.colums)  #查库补齐效果
  print(orderDf.tail(5))

我没实际在使用上面的例子做测试,而是再另一个场景下使用了这种方法,实际对比,原来apply使用200秒的一个需求,换成这里的merge以后,降到了700毫秒,降了三个数量级!
不过这个方法也有个缺陷,那就是,如果nodePlaces里没有这个nodeId,orderDf的place字段,会被设置为NaN,解决的办法有两个:
1. 使用fillna替换
2. 构建nodeDf的时候,将nodePlaces里没有的nodeId也补进去

[转]python Pandas 常用方法备忘

关键缩写和包导入
缩写:

df:任意的Pandas DataFrame对象
s:任意的Pandas Series对象
导入包:

import pandas as pd
import numpy as np

导入数据
pd.read_csv(filename):从CSV文件导入数据
pd.read_table(filename):从限定分隔符的文本文件导入数据
pd.read_excel(filename):从Excel文件导入数据
pd.read_sql(query, connection_object):从SQL表/库导入数据
pd.read_json(json_string):从JSON格式的字符串导入数据
pd.read_html(url):解析URL、字符串或者HTML文件,抽取其中的tables表格
pd.read_clipboard():从你的粘贴板获取内容,并传给read_table()
pd.DataFrame(dict):从字典对象导入数据,Key是列名,Value是数据

导出数据
df.to_csv(filename):导出数据到CSV文件
df.to_excel(filename):导出数据到Excel文件
df.to_sql(table_name, connection_object):导出数据到SQL表
df.to_json(filename):以Json格式导出数据到文本文件

创建测试对象
pd.DataFrame(np.random.rand(20,5)):创建20行5列的随机数组成的DataFrame对象
pd.Series(my_list):从可迭代对象my_list创建一个Series对象
df.index = pd.date_range(‘1900/1/30’, periods=df.shape[0]):增加一个日期索引

查看、检查数据
df.head(n):查看DataFrame对象的前n行
df.tail(n):查看DataFrame对象的最后n行
df.shape():查看行数和列数
df.info():查看索引、数据类型和内存信息
df.describe():查看数值型列的汇总统计
s.value_counts(dropna=False):查看Series对象的唯一值和计数
df.apply(pd.Series.value_counts):查看DataFrame对象中每一列的唯一值和计数

数据选取
df[col]:根据列名,并以Series的形式返回列
df[[col1, col2]]:以DataFrame形式返回多列
s.iloc[0]:按位置选取数据
s.loc[‘index_one’]:按索引选取数据
df.iloc[0,:]:返回第一行
df.iloc[0,0]:返回第一列的第一个元素

数据清理
df.columns = [‘a’,’b’,’c’]:重命名列名
pd.isnull():检查DataFrame对象中的空值,并返回一个Boolean数组
pd.notnull():检查DataFrame对象中的非空值,并返回一个Boolean数组
df.dropna():删除所有包含空值的行
df.dropna(axis=1):删除所有包含空值的列
df.dropna(axis=1,thresh=n):删除所有小于n个非空值的行
df.duplicated():判断重复数据记录
df.drop_duplicates():删除数据记录,可指定特定列或全部
df.fillna(x):用x替换DataFrame对象中所有的空值
s.astype(float):将Series中的数据类型更改为float类型
s.replace(1,’one’):用‘one’代替所有等于1的值
s.replace([1,3],[‘one’,’three’]):用’one’代替1,用’three’代替3
df.rename(columns=lambda x: x + 1):批量更改列名
df.rename(columns={‘old_name’: ‘new_ name’}):选择性更改列名
df.set_index(‘column_one’):更改索引列
df.rename(index=lambda x: x + 1):批量重命名索引

数据处理:Filter、Sort和GroupBy
df[df[col] > 0.5]:选择col列的值大于0.5的行
df.sort_values(col1):按照列col1排序数据,默认升序排列
df.sort_values(col2, ascending=False):按照列col1降序排列数据
df.sort_values([col1,col2], ascending=[True,False]):先按列col1升序排列,后按col2降序排列数据
df.groupby(col):返回一个按列col进行分组的Groupby对象
df.groupby([col1,col2]):返回一个按多列进行分组的Groupby对象
df.groupby(col1)[col2]:返回按列col1进行分组后,列col2的均值
df.pivot_table(index=col1, values=[col2,col3], aggfunc=max):创建一个按列col1进行分组,并计算col2和col3的最大值的数据透视表
df.groupby(col1).agg(np.mean):返回按列col1分组的所有列的均值
data.apply(np.mean):对DataFrame中的每一列应用函数np.mean
data.apply(np.max,axis=1):对DataFrame中的每一行应用函数np.max

数据合并
df1.append(df2):将df2中的行添加到df1的尾部
df.concat([df1, df2],axis=1):将df2中的列添加到df1的尾部
df1.join(df2,on=col1,how=’inner’):对df1的列和df2的列执行SQL形式的join

数据统计
df.describe():查看数据值列的汇总统计
df.mean():返回所有列的均值
df.corr():返回列与列之间的相关系数
df.count():返回每一列中的非空值的个数
df.max():返回每一列的最大值
df.min():返回每一列的最小值
df.median():返回每一列的中位数
df.std():返回每一列的标准差

转自:https://www.cnblogs.com/feiqixia/p/11241925.html

Untiy笔记–鼠标跟随移动

这里的使用场景是飞机的移动,飞机跟随鼠标移动,但飞机会有移动速度上限.
实际使用时,最大速度bigSpeed的数值可以通过unity开发工具设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
    public float bigSpeed; //每毫秒最大的移动距离
    private Transform fighterTransform; //游戏对象
    private long lastUpdate; //记录最近一次update的时间
 
    private void Update(){
        //因为仅需要每次移动时刷新位置,所以不需要放在FixedUpdate里
        FollowMouseMove();
    }
 
    private void FollowMouseMove(){
        long now = (System.DateTime.Now.Ticks - 621355968000000000)/10000;
        //首先获取飞机当前位置
        Vector3 fightPos = fighterTransform.position;
        //鼠标当前位置,ScreenToWorldPoint用于转化为游戏内坐标体系
        Vector3 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
        //2D游戏,抛去z轴信息
        mousePos = new Vector3(mousePos.x, mousePos.y, 0f);
        //飞机鼠标位置的距离
        float distance = Vector3.Distance(fightPos-fix, mousePos);
        //两帧之间最大移动距离,bigSpeed是每毫秒的最大移动距离 Update的调用时间并不准确,为了速度均衡,这里根据时间差值来计算
        float maxLen = ((float)(now - lastUpdate))*bigSpeed;
        Vector3 targetPos; //飞机的目标位置
        //飞机向鼠标位置移动  未超过最大距离
        if(distance <= maxLen){
            targetPos = mousePos;
        }else{
            //三角计算
            float dx = (maxLen*(mousePos.x-fightPos.x))/distance;
            float dy = (maxLen*(mousePos.y-fightPos.y))/distance;
            Vector3 dpos = new Vector3(dx, dy, 0f);
            targetPos = fighterTransform.position + dpos;
        }
        //TODO:targetPos是否超出屏幕范围的判断
        fighterTransform.position = targetPos; //将目标位置复制的游戏对象
        lastUpdate = now;
    }

充分使用Vector3自带运算,对计算过程做精简

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    private void FollowMouseMove(){
        long now = (System.DateTime.Now.Ticks - 621355968000000000)/10000;
        Vector3 fightPos = fighterTransform.position;
        Vector3 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);  //WorldToScreenPoint
        mousePos = new Vector3(mousePos.x, mousePos.y, 0f);
        float distance = Vector3.Distance(fightPos, mousePos); //当前距离
        float rank;
        float maxLen = ((float)(now - lastUpdate))*bigSpeed; //实际可以移动的最大距离
        if(distance < maxLen){
            rank = 1;
        }else{
            rank = maxLen/distance;
        }
        Vector3 dpos = (mousePos - fightPos)*rank;
        Vector3 targetPos = fighterTransform.position + dpos;
        //CanNotOUtCamera 出界判断模块
        targetPos = CanNotOUtCamera(targetPos, fighterTransform.localScale);
        fighterTransform.position = targetPos;
        lastUpdate = now;
    }