nt25 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nt25-0.1.0/.editorconfig +10 -0
- nt25-0.1.0/.flake8 +2 -0
- nt25-0.1.0/.github/workflows/publish.yml +29 -0
- nt25-0.1.0/.gitignore +15 -0
- nt25-0.1.0/.python-version +1 -0
- nt25-0.1.0/PKG-INFO +34 -0
- nt25-0.1.0/README.md +22 -0
- nt25-0.1.0/pyproject.toml +20 -0
- nt25-0.1.0/src/nt25/__init__.py +7 -0
- nt25-0.1.0/src/nt25/lib/calc.py +104 -0
- nt25-0.1.0/src/nt25/lib/draw.py +141 -0
- nt25-0.1.0/src/nt25/lib/fio.py +116 -0
- nt25-0.1.0/src/nt25/main.py +54 -0
nt25-0.1.0/.editorconfig
ADDED
nt25-0.1.0/.flake8
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
name: Publish
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
tags: ["*"]
|
6
|
+
|
7
|
+
jobs:
|
8
|
+
pypi-publish:
|
9
|
+
runs-on: ubuntu-latest
|
10
|
+
|
11
|
+
permissions:
|
12
|
+
id-token: write
|
13
|
+
|
14
|
+
environment:
|
15
|
+
name: publish
|
16
|
+
url: https://pypi.org/p/ntpy
|
17
|
+
|
18
|
+
steps:
|
19
|
+
- name: Checkout
|
20
|
+
uses: actions/checkout@v4
|
21
|
+
|
22
|
+
- name: Setup
|
23
|
+
uses: astral-sh/setup-uv@v6
|
24
|
+
|
25
|
+
- name: Build
|
26
|
+
run: uv build
|
27
|
+
|
28
|
+
- name: Publish
|
29
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
nt25-0.1.0/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.13
|
nt25-0.1.0/PKG-INFO
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: nt25
|
3
|
+
Version: 0.1.0
|
4
|
+
Summary: Neo's Tools of Python
|
5
|
+
Requires-Python: >=3.13
|
6
|
+
Requires-Dist: matplotlib>=3.10.6
|
7
|
+
Requires-Dist: openpyxl>=3.1.5
|
8
|
+
Requires-Dist: pandas>=2.3.2
|
9
|
+
Requires-Dist: scikit-learn>=1.7.1
|
10
|
+
Requires-Dist: sympy>=1.14.0
|
11
|
+
Description-Content-Type: text/markdown
|
12
|
+
|
13
|
+
# NT.py
|
14
|
+
|
15
|
+
Neo's Tools of Python
|
16
|
+
|
17
|
+
## plans
|
18
|
+
|
19
|
+
- [x] fio
|
20
|
+
- [x] calc
|
21
|
+
- [ ] draw with 3d
|
22
|
+
|
23
|
+
## scripts from init
|
24
|
+
|
25
|
+
```sh
|
26
|
+
uv init
|
27
|
+
uv run ntpy
|
28
|
+
```
|
29
|
+
|
30
|
+
## fun
|
31
|
+
|
32
|
+
$$
|
33
|
+
\sqrt{-1},2^{3},\sum,\pi
|
34
|
+
$$
|
nt25-0.1.0/README.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
[project]
|
2
|
+
name = "nt25"
|
3
|
+
version = "0.1.0"
|
4
|
+
description = "Neo's Tools of Python"
|
5
|
+
readme = "README.md"
|
6
|
+
requires-python = ">=3.13"
|
7
|
+
dependencies = [
|
8
|
+
"matplotlib>=3.10.6",
|
9
|
+
"openpyxl>=3.1.5",
|
10
|
+
"pandas>=2.3.2",
|
11
|
+
"scikit-learn>=1.7.1",
|
12
|
+
"sympy>=1.14.0",
|
13
|
+
]
|
14
|
+
|
15
|
+
[project.scripts]
|
16
|
+
ntpy = "ntpy.main:main"
|
17
|
+
|
18
|
+
[build-system]
|
19
|
+
requires = ["hatchling"]
|
20
|
+
build-backend = "hatchling.build"
|
@@ -0,0 +1,104 @@
|
|
1
|
+
import re
|
2
|
+
from typing import Iterable
|
3
|
+
import numpy as np
|
4
|
+
|
5
|
+
from sympy import symbols, Eq, solve
|
6
|
+
|
7
|
+
from sklearn.metrics import r2_score as r2
|
8
|
+
from sklearn.linear_model import LinearRegression
|
9
|
+
from sklearn.preprocessing import PolynomialFeatures
|
10
|
+
|
11
|
+
|
12
|
+
def str2e(s) -> str:
|
13
|
+
return s if len(s) < 5 else f'{float(s):.4e}'
|
14
|
+
|
15
|
+
|
16
|
+
def fl2el(fl) -> list[str]:
|
17
|
+
return list(map(lambda f: str2e(str(f)), fl))
|
18
|
+
|
19
|
+
|
20
|
+
def poly(x, y, degree=2) -> Iterable:
|
21
|
+
return np.polyfit(x, y, degree).tolist()
|
22
|
+
|
23
|
+
|
24
|
+
def polyResults(coef, x) -> Iterable:
|
25
|
+
return np.poly1d(coef)(x).tolist()
|
26
|
+
|
27
|
+
|
28
|
+
def polyRoots(coef) -> Iterable:
|
29
|
+
return np.roots(coef).tolist()
|
30
|
+
|
31
|
+
|
32
|
+
def xn2y(X, y, degree=2, output=False, foo='xn2y'):
|
33
|
+
poly = PolynomialFeatures(degree=degree, include_bias=False)
|
34
|
+
|
35
|
+
Xa = np.array(X)
|
36
|
+
if len(Xa.shape) == 1:
|
37
|
+
Xa = np.array([X])
|
38
|
+
|
39
|
+
x = Xa.T
|
40
|
+
xp = poly.fit_transform(x)
|
41
|
+
|
42
|
+
model = LinearRegression()
|
43
|
+
model.fit(xp, y)
|
44
|
+
# y1 = model.predict(xp)
|
45
|
+
|
46
|
+
coef = model.coef_
|
47
|
+
names = poly.get_feature_names_out().tolist()
|
48
|
+
|
49
|
+
eq = f'{model.intercept_:.4e} + '
|
50
|
+
for i in range(len(names)):
|
51
|
+
ex = names[i].replace('^', '**').replace(' ', '*')
|
52
|
+
eq += f"({coef[i]:.4e})*{ex} + "
|
53
|
+
|
54
|
+
eq = re.sub(r'x(\d+)', r'x[\1]', eq[:-3])
|
55
|
+
func = _genFunc(foo, 'x', eq)
|
56
|
+
|
57
|
+
y2 = [func(x[i]) for i in range(len(x))]
|
58
|
+
yr2 = r2(y, y2)
|
59
|
+
|
60
|
+
if output:
|
61
|
+
q = eq.replace(' + ', ' + \\\n\t') \
|
62
|
+
.replace('*x', ' * x').replace('e+00', '')
|
63
|
+
|
64
|
+
print(f'{foo}: degree = {degree}, R² = {yr2:.4%}\n y = {q}\n')
|
65
|
+
|
66
|
+
return {
|
67
|
+
'func': func,
|
68
|
+
'r2': yr2,
|
69
|
+
'eq': eq,
|
70
|
+
}
|
71
|
+
|
72
|
+
|
73
|
+
def _genFunc(name, args, expr):
|
74
|
+
local = {}
|
75
|
+
src = compile(f'def {name}({args}):\n\treturn {expr}', "<string>", "exec")
|
76
|
+
exec(src, {}, local)
|
77
|
+
return local[name]
|
78
|
+
|
79
|
+
|
80
|
+
def solveEq(eq, output=False, foo='solve'):
|
81
|
+
(x0, x1, y) = symbols('x:2,y')
|
82
|
+
eq = re.sub(r'x\[(\d+)\]', r'x\1', eq)
|
83
|
+
solution = solve(Eq(eval(eq), y), x0)
|
84
|
+
|
85
|
+
real = []
|
86
|
+
for s in solution:
|
87
|
+
sol = str(s)
|
88
|
+
if 'I' in sol:
|
89
|
+
continue
|
90
|
+
|
91
|
+
func = _genFunc(foo, 'y, x1=0', sol)
|
92
|
+
s4e = re.sub(r'([-]?\d*\.\d+)', lambda m: str2e(m.group(0)), sol)
|
93
|
+
|
94
|
+
if output:
|
95
|
+
print(f'{foo}: {eq}\n x0 = {s4e}\n')
|
96
|
+
|
97
|
+
real.append({
|
98
|
+
'org': eq,
|
99
|
+
'func': func,
|
100
|
+
'eq': sol,
|
101
|
+
'eq.4e': s4e,
|
102
|
+
})
|
103
|
+
|
104
|
+
return real
|
@@ -0,0 +1,141 @@
|
|
1
|
+
from enum import Enum
|
2
|
+
from random import sample
|
3
|
+
|
4
|
+
import numpy as np
|
5
|
+
|
6
|
+
from matplotlib.axes import Axes
|
7
|
+
from matplotlib import pyplot as plot
|
8
|
+
|
9
|
+
# import matplotlib.animation as animation
|
10
|
+
|
11
|
+
|
12
|
+
FONTS = ['PingFang SC', 'Microsoft YaHei', 'Arial']
|
13
|
+
COLORS = ['blue', 'red', 'orange', 'black', 'pink']
|
14
|
+
|
15
|
+
|
16
|
+
def _getPlot(ref, pos, font='Roboto') -> Axes:
|
17
|
+
if 'figure' not in ref:
|
18
|
+
ref['figure'] = plot.figure()
|
19
|
+
plot.rcParams['font.sans-serif'] = [font] + FONTS
|
20
|
+
# plot.rcParams['font.sans-serif'] = ['Arial']
|
21
|
+
plot.rcParams['axes.unicode_minus'] = False
|
22
|
+
|
23
|
+
if 'subplot' not in ref:
|
24
|
+
ref['subplot'] = {}
|
25
|
+
|
26
|
+
fig = ref['figure']
|
27
|
+
sub = ref['subplot']
|
28
|
+
|
29
|
+
if pos not in sub:
|
30
|
+
sub[pos] = fig.add_subplot(pos)
|
31
|
+
|
32
|
+
return sub[pos]
|
33
|
+
|
34
|
+
|
35
|
+
def _genList(refer: list, length, random=False):
|
36
|
+
r = []
|
37
|
+
|
38
|
+
while len(r) < length:
|
39
|
+
r += sample(refer, len(refer)) if random else refer
|
40
|
+
|
41
|
+
return r[:length]
|
42
|
+
|
43
|
+
|
44
|
+
def _coord(ref, X, Y, color, pos, randomColor, method, *args, **kwargs):
|
45
|
+
Xa = np.array(X)
|
46
|
+
Ya = np.array(Y)
|
47
|
+
|
48
|
+
if Xa.shape != Ya.shape:
|
49
|
+
print(f"bad shape {Xa.shape} != {Ya.shape}")
|
50
|
+
return
|
51
|
+
|
52
|
+
count = 1
|
53
|
+
# count = Xa.shape[0] if len(Xa.shape) > 1 else 1
|
54
|
+
|
55
|
+
if len(Xa.shape) > 1:
|
56
|
+
count = Xa.shape[0]
|
57
|
+
if count == 1:
|
58
|
+
X = X[0]
|
59
|
+
Y = Y[0]
|
60
|
+
|
61
|
+
if color is None:
|
62
|
+
color = _genList(COLORS, count, random=randomColor)
|
63
|
+
|
64
|
+
if count == 1:
|
65
|
+
if isinstance(color, (list, tuple)):
|
66
|
+
color = color[0]
|
67
|
+
elif len(color) != count:
|
68
|
+
print(f"bad color.len {len(color)} != {count}")
|
69
|
+
return
|
70
|
+
|
71
|
+
if pos is None:
|
72
|
+
pos = [111] * count
|
73
|
+
|
74
|
+
if count == 1:
|
75
|
+
if isinstance(pos, (list, tuple)):
|
76
|
+
pos = pos[0]
|
77
|
+
elif len(pos) != count:
|
78
|
+
print(f"bad pos.len {len(pos)} != {count}")
|
79
|
+
return
|
80
|
+
|
81
|
+
if count > 1 and isinstance(pos, list):
|
82
|
+
for i in range(count):
|
83
|
+
p = _getPlot(ref, pos[i])
|
84
|
+
method(p, X[i], Y[i], color=color[i], *args, **kwargs)
|
85
|
+
else:
|
86
|
+
p = _getPlot(ref, pos)
|
87
|
+
method(p, X, Y, color=color, *args, **kwargs)
|
88
|
+
|
89
|
+
return ref
|
90
|
+
|
91
|
+
|
92
|
+
class DType(Enum):
|
93
|
+
scatter = 1,
|
94
|
+
line = 2,
|
95
|
+
func = 3,
|
96
|
+
|
97
|
+
|
98
|
+
def d2d(type=DType.scatter, X=None, Y=None, Func=None, min=None, max=None,
|
99
|
+
ref=None, color=None, pos=None, randomColor=False, show=False,
|
100
|
+
*args, **kwargs):
|
101
|
+
if ref is None:
|
102
|
+
ref = {}
|
103
|
+
|
104
|
+
match type:
|
105
|
+
case DType.scatter:
|
106
|
+
func = Axes.scatter
|
107
|
+
|
108
|
+
case DType.line:
|
109
|
+
func = Axes.plot
|
110
|
+
|
111
|
+
case DType.func:
|
112
|
+
func = Axes.plot
|
113
|
+
if Func is not None and min is not None and max is not None:
|
114
|
+
if callable(Func):
|
115
|
+
Func = (Func,)
|
116
|
+
|
117
|
+
X = []
|
118
|
+
Y = []
|
119
|
+
|
120
|
+
for i in range(len(Func)):
|
121
|
+
dx = np.linspace(min[i] if isinstance(min, (list, tuple)) else min,
|
122
|
+
max[i] if isinstance(max, (list, tuple)) else max)
|
123
|
+
|
124
|
+
X.append(dx)
|
125
|
+
Y.append([Func[i]([x]) for x in dx])
|
126
|
+
|
127
|
+
ref = _coord(ref, X, Y, color=color, pos=pos, randomColor=randomColor,
|
128
|
+
method=func, *args, **kwargs)
|
129
|
+
|
130
|
+
if show:
|
131
|
+
plot.show()
|
132
|
+
|
133
|
+
return ref
|
134
|
+
|
135
|
+
|
136
|
+
def show():
|
137
|
+
plot.show()
|
138
|
+
|
139
|
+
|
140
|
+
def clear():
|
141
|
+
plot.clf()
|
@@ -0,0 +1,116 @@
|
|
1
|
+
import os
|
2
|
+
import csv
|
3
|
+
from typing import Iterable
|
4
|
+
|
5
|
+
# import openpyxl
|
6
|
+
import pandas as pd
|
7
|
+
|
8
|
+
ENCODINGS = ['utf-8', 'utf-8-sig', 'gbk']
|
9
|
+
|
10
|
+
|
11
|
+
def _switchEnc(foo, encoding=None, *args, **kwargs):
|
12
|
+
result = None
|
13
|
+
|
14
|
+
if encoding is None:
|
15
|
+
for enc in ENCODINGS if os.name != 'nt' else reversed(ENCODINGS):
|
16
|
+
try:
|
17
|
+
result = foo(encoding=enc, *args, **kwargs)
|
18
|
+
break
|
19
|
+
except Exception as e:
|
20
|
+
print(e)
|
21
|
+
|
22
|
+
else:
|
23
|
+
result = foo(encoding=encoding, *args, **kwargs)
|
24
|
+
|
25
|
+
return result
|
26
|
+
|
27
|
+
|
28
|
+
def _getCSV(file, width, startLine, startCol, encoding):
|
29
|
+
f = open(file, encoding=encoding)
|
30
|
+
cf = csv.reader(f)
|
31
|
+
|
32
|
+
count = 0
|
33
|
+
result = [[] for _ in range(width - startCol)]
|
34
|
+
|
35
|
+
for line in cf:
|
36
|
+
if count >= startLine:
|
37
|
+
maxWidth = len(line)
|
38
|
+
for i in range(startCol, width):
|
39
|
+
x = line[i].strip() if maxWidth > i else ''
|
40
|
+
|
41
|
+
try:
|
42
|
+
result[i - startCol].append(float(x))
|
43
|
+
except ValueError:
|
44
|
+
result[i - startCol].append(x)
|
45
|
+
|
46
|
+
count += 1
|
47
|
+
|
48
|
+
f.close()
|
49
|
+
return result
|
50
|
+
|
51
|
+
|
52
|
+
def _getCSV2(file, encoding=None, colsInline=True):
|
53
|
+
df = pd.read_csv(file, encoding=encoding)
|
54
|
+
|
55
|
+
row = df.to_numpy()
|
56
|
+
r = row.T if colsInline else row
|
57
|
+
|
58
|
+
return r.tolist()
|
59
|
+
|
60
|
+
|
61
|
+
def getCSV(file, width=2, startLine=1, startCol=0, encoding=None):
|
62
|
+
return _switchEnc(foo=_getCSV, encoding=encoding, file=file,
|
63
|
+
width=width, startLine=startLine, startCol=startCol)
|
64
|
+
|
65
|
+
|
66
|
+
def getCSV2(file, encoding=None):
|
67
|
+
return _switchEnc(foo=_getCSV2, encoding=encoding, file=file)
|
68
|
+
|
69
|
+
|
70
|
+
def saveCSV(data, file, encoding=None, colsInline=True):
|
71
|
+
if encoding is None:
|
72
|
+
encoding = 'utf-8'
|
73
|
+
|
74
|
+
with open(file, 'w', newline='', encoding=encoding) as f:
|
75
|
+
content = []
|
76
|
+
|
77
|
+
for d in data:
|
78
|
+
if isinstance(d, str):
|
79
|
+
content.append(data[d])
|
80
|
+
elif isinstance(d, Iterable):
|
81
|
+
content.append(d)
|
82
|
+
|
83
|
+
if colsInline:
|
84
|
+
content = list(map(list, zip(*content)))
|
85
|
+
|
86
|
+
cf = csv.writer(f)
|
87
|
+
cf.writerows(content)
|
88
|
+
|
89
|
+
|
90
|
+
def saveCSV2(data, file, encoding=None, float_format=None, colsInline=True):
|
91
|
+
if data is not None:
|
92
|
+
df = pd.DataFrame(data)
|
93
|
+
|
94
|
+
if colsInline:
|
95
|
+
df = df.T
|
96
|
+
|
97
|
+
df.to_csv(file, index=False, encoding=encoding, float_format=float_format)
|
98
|
+
|
99
|
+
|
100
|
+
def getXlsx(file, sheet=0, colsInLine=True):
|
101
|
+
df = pd.read_excel(file, sheet_name=sheet)
|
102
|
+
|
103
|
+
row = df.to_numpy()
|
104
|
+
r = row.T if colsInLine else row
|
105
|
+
|
106
|
+
return r.tolist()
|
107
|
+
|
108
|
+
|
109
|
+
def saveXlsx(data, file, colsInline=True):
|
110
|
+
if data is not None:
|
111
|
+
df = pd.DataFrame(data)
|
112
|
+
|
113
|
+
if colsInline:
|
114
|
+
df = df.T
|
115
|
+
|
116
|
+
df.to_excel(file, index=False)
|
@@ -0,0 +1,54 @@
|
|
1
|
+
from ntpy import fio, calc, draw, DType, __version__
|
2
|
+
|
3
|
+
# import timeit
|
4
|
+
# timeit.timeit('', number=100, globals=globals())
|
5
|
+
|
6
|
+
|
7
|
+
def main():
|
8
|
+
print(f"Hello from {__package__}({__version__})! ")
|
9
|
+
|
10
|
+
# [Neo] some file I/O: csv, xlsx
|
11
|
+
fio.saveCSV({
|
12
|
+
'Name': ['Alice', 'Bob', 'Charlie'],
|
13
|
+
'Age': [25.2, 30, 35],
|
14
|
+
'City': ['New York', 'Los Angeles', 'Chicago']
|
15
|
+
}, "out.csv", colsInline=False)
|
16
|
+
|
17
|
+
data = fio.getXlsx('ds/qs.xlsx')
|
18
|
+
fio.saveCSV(data, "out2.csv")
|
19
|
+
|
20
|
+
h = data[0]
|
21
|
+
s = data[1]
|
22
|
+
v = data[2]
|
23
|
+
|
24
|
+
# [Neo] some poly calculates
|
25
|
+
c = calc.poly(h, v)
|
26
|
+
ce = calc.fl2el(c)
|
27
|
+
print(c, ce)
|
28
|
+
|
29
|
+
foo = calc.xn2y(h, v, degree=3, output=True)
|
30
|
+
bar = calc.solveEq(foo['eq'])
|
31
|
+
|
32
|
+
func = foo['func']
|
33
|
+
bfunc = bar[0]['func']
|
34
|
+
|
35
|
+
Y = range(1000, 2000, 200)
|
36
|
+
X = [bfunc(y) for y in Y]
|
37
|
+
|
38
|
+
# [Neo] draw 2d with types
|
39
|
+
ref = {}
|
40
|
+
draw.d2d(X=h, Y=v, ref=ref)
|
41
|
+
draw.d2d(type=DType.scatter, X=X, Y=Y, ref=ref, color='red', s=120)
|
42
|
+
draw.d2d(type=DType.func, Func=func, min=40, max=60, ref=ref, color='red')
|
43
|
+
draw.show()
|
44
|
+
|
45
|
+
# [Neo] and 3d calcs
|
46
|
+
foo = calc.xn2y([h, s], v, degree=3, output=False)
|
47
|
+
bar = calc.solveEq(foo['eq'], output=True)
|
48
|
+
|
49
|
+
if len(bar) > 0:
|
50
|
+
print('s> 750, 1.5 ~', bar[0]['func'](y=750, x1=1.5))
|
51
|
+
|
52
|
+
|
53
|
+
if __name__ == "__main__":
|
54
|
+
main()
|