将 DataFrame 的每一列的(列标题和每一行元素)打包成一个元组
将 DataFrame 的(列标题)和
def fun_zip(
self,
df_arg=pd.DataFrame(),
bool_arg=False
):
# 每列元素打包
zipped = zip(*[df_arg.iloc[i] for i in range(df_arg.shape[0])])
# 是否包含标题
if bool_arg:
list_zip = [(col_name, *row) for col_name, row in zip(df_arg.columns, zipped)]
else:
list_zip = [tuple(row) for row in zipped]
return list_zip
这段代码的目的是将一个 DataFrame 的每一列打包成一个元组,然后返回这些元组的列表。如果 bool_arg
为 True
,则每个元组的第一个元素是列名。
这段代码已经很好地完成了这个任务,但是有一些地方可以进行优化:
- 默认参数:在函数定义中,尽量避免使用可变的默认参数,比如这里的
df_arg=pd.DataFrame()
。这是因为默认参数在 Python 中只会被评估一次,如果你对默认参数进行了修改,那么这个修改会在下次调用函数时保留。所以,更好的做法是将默认参数设置为None
,然后在函数体中检查这个参数是否为None
,如果是,则赋予其默认值。 - 使用
itertools.zip_longest
:在你的代码中,你使用了zip
函数来打包每一列。然而,如果 DataFrame 的列长度不一致,zip
函数会忽略多出来的元素。为了避免这种情况,你可以使用itertools.zip_longest
函数,它会用None
来填充短的列。 - 使用
DataFrame.itertuples
:Pandas 提供了一个itertuples
方法,可以更高效地迭代 DataFrame 的行。你可以使用这个方法来替代zip(*[df_arg.iloc[i] for i in range(df_arg.shape[0])])
。
考虑到以上的优化建议,你的代码可以改写成如下形式:
from itertools import zip_longest
def fun_zip(
self,
df_arg=None,
bool_arg=False
):
if df_arg is None:
df_arg = pd.DataFrame()
# 每列元素打包
zipped = zip_longest(*df_arg.itertuples(index=False, name=None))
# 是否包含标题
if bool_arg:
list_zip = [(col_name, *row) for col_name, row in zip(df_arg.columns, zipped)]
else:
list_zip = [row for row in zipped]
return list_zip
在你提供的代码 zipped = zip_longest(*df_arg.itertuples(index=False, name=None))
中,zip_longest
函数会返回一个迭代器,每个元素都是一个元组,元组中的元素对应于 df_arg
的每一行。
[tuple(row) for row in zipped]
:这段代码会将zipped
中的每个元素(已经是元组)转换为元组,所以这里的tuple(row)
实际上没有改变row
的数据类型。这意味着,最后得到的是一个元组的列表,列表中的每个元组对应于df_arg
的一行。[row for row in zipped]
:这段代码会直接将zipped
中的元素(元组)放入列表中,不进行任何类型转换。这意味着,最后得到的也是一个元组的列表,列表中的每个元组对应于df_arg
的一行。
所以,在这个特定的情况下,[tuple(row) for row in zipped]
和 [row for row in zipped]
的结果是一样的,因为 zipped
中的元素已经是元组。
在 Python 中,self
是一个约定俗成的名字,用于指代实例本身。在类的方法中,我们通常将 self
作为第一个参数,以便可以在方法中引用实例的属性和其他方法。
然而,并不是所有的函数参数都需要前面加 self
。只有当你需要在方法中访问或修改实例的属性时,才需要使用 self
。如果一个变量只在方法内部使用,那么就不需要 self
。
例如,以下是一个类的定义,其中 self.name
是一个实例属性,而 greeting
只是 say_hello
方法的一个局部变量:
class Person:
def __init__(self, name):
self.name = name
def say_hello(self):
greeting = "Hello, " + self.name
print(greeting)
在这个例子中,self.name
是需要 self
的,因为它是一个实例属性,我们希望在类的其他方法中也能访问到它。而 greeting
只在 say_hello
方法中使用,所以它不需要 self
。
总的来说,是否在变量前加 self
,取决于你是否希望这个变量在整个实例中都可见。如果是,那么就需要 self
;如果只是方法内部的临时变量,那么就不需要 self
。
你遇到的问题是由于 Python 的列表推导式(list comprehension)中不支持解包(unpacking)。在你的代码 [*row for row in zipped]
中,*row
是试图解包 row
,但是在列表推导式中这是不被允许的。
如果你想要得到一个列表,其中每个元素都是 zipped
中的一个元组,你可以直接使用 list(zipped)
,或者使用列表推导式 [row for row in zipped]
。这两种方式都可以得到相同的结果。
如果你想要将 zipped
中的所有元素解包成一个列表,你可以在列表推导式外部进行解包,像这样:[*zipped]
。这样,你会得到一个列表,其中包含了 zipped
中所有元组的所有元素。
这段代码的目的是将 DataFrame 的每一列打包成一个元组,然后返回这些元组的列表。如果 bool_arg
为 True
,则每个元组的第一个元素是列名。
检查并优化
def fun_zip(
self,
df_arg=None,
bool_arg=True
):
if df_arg is None:
df_arg = pd.DataFrame()
# 每列元素打包
zipped = zip_longest(*df_arg.itertuples(index=False, name=None))
list_zip = list(zipped)
# 是否包含标题
if bool_arg:
list_zip = [(col_name, *row) for col_name, row in zip(df_arg.columns, zipped)]
return list_zip
这段代码已经很好地完成了这个任务,但是有一点需要注意:
在你的代码中,你先创建了 list_zip
,然后在 if bool_arg:
的条件下可能会覆盖它。但是,如果 bool_arg
为 True
,zip_longest(*df_arg.itertuples(index=False, name=None))
这个迭代器在创建 list_zip
时已经被耗尽,所以在 zip(df_arg.columns, zipped)
中,zipped
是空的。这意味着,如果 bool_arg
为 True
,你的函数会返回一个空列表。
为了解决这个问题,你可以在 if bool_arg:
的条件下重新创建 zipped
,像这样:
from itertools import zip_longest
import pandas as pd
def fun_zip(
self,
df_arg=None,
bool_arg=True
):
if df_arg is None:
df_arg = pd.DataFrame()
# 每列元素打包
zipped = zip_longest(*df_arg.itertuples(index=False, name=None))
# 是否包含标题
if bool_arg:
list_zip = [(col_name, *row) for col_name, row in zip(df_arg.columns, list(zipped))]
else:
list_zip = list(zipped)
return list_zip
这样,无论 bool_arg
的值是什么,你的函数都会返回正确的结果。希望这个建议对你有所帮助!
在 Python 中,迭代器是一个可以记住遍历的位置的对象。迭代器从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退,不像列表或者数组,迭代器不会一次性把所有元素加载到内存,而是需要时才生成元素。
迭代器(比如你在代码中使用的 zip_longest
函数返回的对象)是一次性的。这意味着迭代器中的元素在被访问后就会被丢弃,这就是所谓的 “迭代器被耗尽”。如果你试图再次从同一个迭代器中获取元素,你会发现它已经没有元素了,因为它们已经被访问并丢弃了。
例如,考虑以下代码:
my_list = [1, 2, 3]
my_iter = iter(my_list)
for item in my_iter:
print(item)
for item in my_iter:
print(item)
在这个例子中,第一个 for
循环会打印出列表中的所有元素。但是第二个 for
循环不会打印任何东西,因为迭代器已经被耗尽,所有的元素都已经被访问并丢弃了。
所以,如果你想要再次访问迭代器中的元素,你需要重新创建迭代器。希望这个解释能帮助你理解!
df.itertuples()
是 pandas DataFrame 的一个方法,它会返回一个迭代器,迭代器中的每个元素都是一个元组,元组中的元素对应于 DataFrame 的一行¹²。
这个方法有两个参数:
index
:默认为True
。如果为True
,则返回的元组的第一个元素是索引¹。name
:默认为'Pandas'
。这个参数可以设置返回的 namedtuple 的名称。如果为None
,则返回普通的元组¹。
例如,假设我们有一个 DataFrame df
:
import pandas as pd
df = pd.DataFrame({
'num_legs': [4, 2],
'num_wings': [0, 2]
}, index=['dog', 'hawk'])
我们可以使用 itertuples()
来迭代 DataFrame 的行:
for row in df.itertuples():
print(row)
输出:
Pandas(Index='dog', num_legs=4, num_wings=0)
Pandas(Index='hawk', num_legs=2, num_wings=2)
如果我们设置 index=False
,则可以去掉索引:
for row in df.itertuples(index=False):
print(row)
输出:
Pandas(num_legs=4, num_wings=0)
Pandas(num_legs=2, num_wings=2)
希望这个解释能帮助你理解!
(1) pandas.DataFrame.itertuples — pandas 2.1.3 documentation. https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.itertuples.html.
(2) Pandas DataFrame itertuples() Method - W3Schools. https://www.w3schools.com/python/pandas/ref_df_itertuples.asp.
(3) iterate over pandas dataframe using itertuples - Stack Overflow. https://stackoverflow.com/questions/43221208/iterate-over-pandas-dataframe-using-itertuples.
标签:zip,df,zipped,DataFrame,元组,arg,row From: https://blog.51cto.com/u_16055028/8497509