py tips

image

from pixiv

Python Bases

Python Data Types

格式化输出

在 Python 中,格式化浮点数通常使用 f 或 g 类型说明符:

  • f:​表示定点数格式,始终以小数点表示。例如,{value:.6f} 会将 value 格式化为小数点后 6 位的浮点数。​
  • g:​表示通用格式,根据数值的大小自动选择定点数格式或科学计数法。例如,{value:.6g} 会将 value 格式化为 6 位有效数字,并根据需要选择最合适的表示方式。(如 123.456)或科学计数法(如 1.23456e+02)。

String and Methords

range and list and dict

dict = {'a':1, 'b':2, 'c':3, 'd':1}
values = dict.values()
print(values)

output: dict_values([1, 2, 3, 1])

返回dict_values对象,其是一个可迭代对象,同时可通过list(values)得到列表

tips: list() 是一个内置函数,任何可迭代对象(iterable)可以通过 list() 转换为列表。
more detail

了解上述知识后就很好理解如下常见代码:

for i in range(5):
	print(i)

range 是一个内建的函数,返回的是一个 range 对象, range为可迭代对象。
所有可迭代对象均可如上使用for进行循环迭代。


上述dict.values()是获得字典中value的方法,那有没有什么方法可以获得key呢?

dict = {'a':1, 'b':2, 'c':3, 'd':1}
keys = dict.keys()
print(keys)

output: dict_keys(['a', 'b', 'c', 'd'])

python中同样可以使用dict[a] dict[a] = 2访问和修改dict

tips: 在 Python 中,如果你在一个类中实现了 getitemsetitem 方法,你的类将支持索引操作和项赋值操作。这意味着你可以使用方括号([])来获取和设置对象的属性.

class MyDict:
    def __init__(self):
        self.data = {}

    def __getitem__(self, key):
        print(f"Getting value for key: {key}")
        return self.data[key]  # 获取字典中的值

    def __setitem__(self, key, value):
        print(f"Setting value for key: {key} to {value}")
        self.data[key] = value  # 设置字典中的值

__getitem__ 方法定义了如何通过索引来访问对象中的元素。当你使用 obj[key] 语法时,Python 会调用 obj.__getitem__(key) 方法。
__setitem__ 方法定义了如何通过索引来修改对象中的元素。当你使用 obj[key] = value 语法时,Python 会调用 obj.__setitem__(key, value) 方法。


列表推导式(List Comprehension): 创建列表的语法糖, 基本结构如下:

#[expression for item in iterable if condition]

nodes_values = [
    {
        "func_name": x.func_name,
        "module_name": x.module_name,
        "cycles": x.get_cycles(),
    }
    for x in graph.nodes()
]

expression通常为一个对象,这里为一个字典对象。
for x in graph.nodes()for item in iterable

生成表达式

生成器表达式是一种惰性计算的语法结构,用于按需生成值,而不是一次性生成所有值。它的语法与列表推导式(List Comprehension)非常相似,但使用圆括号 () 而不是方括号 []

(表达式 for 变量 in 可迭代对象 if 条件)

示例

# 生成器表达式
gen = (x * 2 for x in range(5) if x % 2 == 0)

# 输出
for value in gen:
    print(value)

输出:

0
4
8

生成器表达式是迭代器
生成器表达式返回的是一个生成器对象,而生成器对象是 Python 中的一种迭代器。因此,生成器表达式具有迭代器的所有特性:

迭代器的特性:

  1. 惰性计算:值是按需生成的,而不是一次性生成所有值。
  2. 只能遍历一次:生成器对象在遍历完后会被耗尽,无法再次使用。
  3. 支持 next() 函数:可以通过 next() 逐个获取值。
gen = (x for x in range(3))

# 使用 next() 逐个获取值
print(next(gen))  # 输出 0
print(next(gen))  # 输出 1
print(next(gen))  # 输出 2
print(next(gen))  # 抛出 StopIteration 异常

Python Iterators

可迭代对象(Iterable objects)是一个容器,我们使用iter()方法可得到迭代器(Iterator)。Lists, tuples, dictionaries, and sets这些数据类型都是可迭代对象。

在 Python 中,要使一个类成为可迭代对象(Iterable),需要在类中实现 __iter__ 方法,且这个方法应该返回一个迭代器,迭代器本身必须实现 __next__ 方法。

迭代器是一个包含__iter__ 方法和__next__ 方法的对象。
__iter__进行初始化操作最终返回一个迭代器
__next__最终返回下一个元素

class MyIterable:
	def __init__(self, start, end):
		self.start = start
		self.end = end
	def __iter__(self):
		return self
	def __next__(self):
		if self.start >= self.end:
			raise StopIteration
		self.start += 1
		return self.start - 1

上述例子中,我定义了一个类MyIterable,在__iter__其返回了一个迭代器即自身(因为MyIterable也实现了__next__所以MyIterable也算迭代器)。所以实现了MyIterable为一个可迭代对象。

class NodeTable:
	def __init__(self, nodes: Dict[str, Node] | None = None):
		self._nodes: Dict[str, Node] = node if nodes else {}
	def __iter__(self):
		return iter(self._nodes)

上述例子中__iter__中因为dictionaries本身就是可迭代对象,通过iter()得到字典的迭代器并返回,所以使得NodeTable成功实现为了可迭代对象


可迭代对象使用迭代器能够通过next函数访问元素:

mytuple = ("apple", "banana", "cherry")
myit = iter(mytuple)

print(next(myit))
print(next(myit))
print(next(myit))

more details

Python lamba表达式

df["semantics"] = df["symbol"].map( 
    lambda x: next((k for k, v in semantics_mapping.items() if x in v), None) 
)

这段代码的作用是为 DataFrame 的 symbol 列创建一个新列 semantics,其值基于 semantics_mapping 字典的映射。我将用通俗易懂的方式逐步解释每个语法元素,并提供具体示例帮助你理解。

lambda 表达式:

  • lambda 是 Python 中定义匿名函数的关键字,用于快速创建小型临时函数。
  • 语法lambda 参数: 表达式
  • 特点:没有函数名,只能包含单行表达式,自动返回结果。
lambda x: next((k for k, v in semantics_mapping.items() if x in v), None)
  • 参数x 是输入值(即 DataFrame 中 symbol 列的每个值)。
  • 表达式next(...) 是返回值。

next() 函数:

  • next(iterator, default) 用于从迭代器中获取下一个元素。
  • 参数
    • iterator:可迭代对象(如生成器、列表等)。
    • default(可选):当迭代器耗尽时返回的默认值。

生成器表达式 (k for ...)

  • 生成器表达式用于惰性生成值,逐个产生结果,节省内存。
  • 语法(表达式 for 变量 in 可迭代对象 if 条件)

代码中的生成器:

(k for k, v in semantics_mapping.items() if x in v)
  • 遍历semantics_mapping.items() 的每个键值对 (k, v)
  • 条件:如果当前 xsymbol 的值)在 v(字典值的列表)中。
  • 返回值:符合条件的键 k

Python Built-in functions

len

  • 输入:可迭代对象
  • 输出:可迭代对象元素个数
list = [1,2,3,4]
print(f"len: {len(list)}")

df = pd.read_csv(file)
print(f"rownum: {len(df)}")

map

  • 输入:可迭代对象
  • 输出:迭代器
  • 作用:对一个可迭代对象(例如列表、元组、字符串等)中的每个元素应用一个指定的函数,并返回一个迭代器,其每个元素都是函数作用后的结果。
list = [1,2,3,4]
list_2 = list(map(lamba x: x*x, list))

join

separator.join(iterable)
  • separator:分隔符字符串。这个字符串将插入到每个被连接的字符串之间。
  • iterable:一个可迭代对象,其中的每个元素都应当是字符串。如果其中有非字符串类型,会抛出异常。
  • 输出:字符串
function = ['add', 'sub','mul']
pattern = '|'.join(function)

Python Pandas

Pandas Basics

Pandas Series and DataFrames

Series对象就像表格中的一列:

index value
x 1
y 7
z 2
values = [1, 7, 2]
myseries = pd.Series(values, index = ["x", "y", "z"])

若去除index,即myseries = pd.Series(values)那么下标默认为1,2,3...
除了list可以转换为series,dict同样可以转换:

dict = {"x":1, "y":7, "z":2}
myseries = pd.Series(dict)

Note: The keys of the dictionary become the labels(index).即上述代码效果和上述表格一致


按照上述思路,DataFrame就像表格:

index Name Age Salary
0 Alice 25 500
1 Bob 30 600
2 Charlie 35 700

列表和字典同样可以转换为DataFrame:

data1 = [
	["Alice", 25, 500],
	["Bob", 30, 600],
	["Charlie", 35, 700]
]
df = pd.DataFrame(data1, columns=["Name", "Age", "Salary"], index=[1,2,3])

data2 = {
    "Name": ["Alice", "Bob", "Charlie"],
    "Age": [25, 30, 35],
    "Salary": [500, 600, 700],
}
df = pd.DataFrame(data2, index=[1,2,3])

simple info

df.head(n) # 表头数据

df.tail(n) # 表尾巴数据

df.info() # 总结各列数据信息
# output:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3 entries, 0 to 2
Data columns (total 3 columns):
 #   Column  Non-Null Count  Dtype
---  ------  --------------  -----
 0   Name    3 non-null      object
 1   Age     3 non-null      int64
 2   Salary  3 non-null      int64
dtypes: int64(2), object(1)
memory usage: 204.0+ bytes
None

Pandas 寻找子集

.isin

.isin() 用于检查 DataFrame 或 Series 中的每个元素是否属于给定的序列(例如列表、集合、Series 等)中的某个值。

import pandas as pd

data = {'symbol': ['AAPL', 'GOOG', 'MSFT', 'AMZN'],
        'price': [150, 2800, 300, 3500]}
symbols_to_keep = ['AAPL', 'MSFT']
filtered_df = df[df['symbol'].isin(symbols_to_keep)]
print(filtered_df)

.str.contains

.str.contains() 用于判断 Series 中每个字符串是否包含指定的子串或正则表达式模式。

import pandas as pd

data = {'symbol': ['AAPL_Inc', 'Google LLC', 'MSFT_Corporation', 'Amazon.com']}
pattern = 'LLC|Corporation'
filtered_df = df[df['symbol'].str.contains(pattern, regex=True, na=False)]
print(filtered_df)

Python Numpy

对于一个 1000 万个元素的数组(成为向量或矢量)乘法操作,NumPy 使用方法跟单个值(称为标量)完全一致。

即对于Numpy不管是x = np.arange(10000000)还是x = np.array([1]),都可以使用x * 2直接对全部元素乘2

这种高效且简易操作的 NumPy 数组其正式对象名为 ndarray

ndarray

创建ndarray

创建ndarray和ndarray的属性

其实从DataFrame或Serial中通过.values得到的也是ndarray:

df = pd.read_csv['xxx']
lab1 = df['lab1'].value()
print(type(lab1))
# 输出
<class 'numpy.ndarray'>

Python Matplotlib

对数分箱和对数轴

对数轴

在此对数能够很好地处理跨越多个数量级、且在低值或高值段分布不均的数据,使得数据图在各个数量级上都能清晰展示数据分布情况。

例如:

from matplotlib import pyplot

a = [ pow(10,i) for i in range(10) ]

pyplot.subplot(2,1,1)
pyplot.plot(a, color='blue', lw=2)

pyplot.show()

image

对于a数据集[1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000],大部分数据都在1~1000000,但是上述图因为y轴数据范围的问题,缺失掉了大部分数据信息

加上pyplot.yscale('log')可解决这个问题:
image

The relevant function is pyplot.yscale(). If you use the object-oriented version, replace it by the method Axes.set_yscale(). Remember that you can also change the scale of X axis, using pyplot.xscale() (or Axes.set_xscale()).
stackover flow Plot logarithmic axes

对数分箱

同样对于低值或高值段分布不均的数据,例如我想要统计这些数据在不同区间上的Count并用直方图显示出来,应该如何办?

第一步需要考虑如下划分区间:

stack overflow: How to have logarithmic bins in a Python histogram

首先可以考虑下numpy中的自动分箱技术:numpy.histogram(a, bins='auto', range=None)

  • a: 输入数据(一维数组)
  • bins: 分箱策略(支持字符串如 'auto', 'fd', 'sturges', 'sqrt' 等)
  • range: 数据范围(默认为数据的最小/最大值)
  • numpy.histogram 的返回值是一个包含两个数组的元组:直方图频数(hist)和分箱边界(bin_edges)
import numpy as np
data = [1, 2, 3, 4]
hist, bin_edges = np.histogram(data, bins=2)

假设输入数据为 data = [1, 2, 3, 4],分箱策略为 bins=2:

  • hist = [2, 2]
    分箱1(1.0 到 2.5):包含数据点 1 和 2 → 计数值为 2。
    分箱2(2.5 到 4.0):包含数据点 3 和 4 → 计数值为 2。

  • bin_edges = [1.0, 2.5, 4.0]
    分箱边界:共 3 个值(2 个分箱需要 3 个边界)。

可用这个直接画出直方图:

import matplotlib.pyplot as plt

data = np.random.randn(1000)
hist, bin_edges = np.histogram(data, bins='auto')

# 绘制直方图
plt.bar(bin_edges[:-1], hist, width=np.diff(bin_edges), edgecolor='black')
plt.title("Histogram with Auto Bins")
plt.show()

若考虑到对数问题,则可以手动在数据上进行log10

import numpy as np
import matplotlib.pyplot as plt

data = 10**np.random.normal(size=500)

_, bins = np.histogram(np.log10(data + 1), bins='auto')
plt.hist(data, bins=10**bins);
plt.gca().set_xscale("log")

data + 1是为了避免数据出现==0的数据


也可以通过np.logspace实现,np.logspace 是 NumPy 提供的生成对数刻度数组的函数,其核心功能是“在对数空间内等距、在原始空间内等比”地生成一系列数值

numpy.logspace(start,      # 对数刻度起点(指数)
               stop,       # 对数刻度终点(指数)
               num=50,     # 生成样本数量
               endpoint=True, # 是否包含 stop 指数对应的值
               base=10.0,  # 对数底,默认 10
               dtype=None,
               axis=0)
			   
			   
import numpy as np

# 例:生成从 10^1 到 10^3 的 3 个对数等距点
log_bins = np.logspace(1, 3, num=3)  
print(log_bins)  # 输出 [   10.   100.  1000.]
# 验证等比关系
ratios = log_bins[1:] / log_bins[:-1]
print(ratios)    # 输出 [10. 10.] 公比恒定为 10
  • 返回值类型:numpy.ndarray,长度为 num,元素为浮点数
  • start, stop:通常取 log10(min_value) 和 log10(max_value);
  • 缺点是需要手动指定num:所需分箱边界数(通常等于分箱数或分箱数+1);

np.logspace和np.联动

bins = np.logspace(np.log10(runtime.min()), np.log10(runtime.max()), num=20)

counts, bin_edges = np.histogram(runtime, bins=bins)

或者选择使用plt.hist直接画出来:

import pylab as pl
import numpy as np

data = np.random.normal(size=10000) # 一个一维ndarray数组
pl.hist(data, bins=np.logspace(np.log10(0.1),np.log10(1.0), 50))
pl.gca().set_xscale("log")
pl.show()
posted @ 2023-08-27 22:36  次林梦叶  阅读(44)  评论(0)    收藏  举报