Python
在 Pandas 中重新排序列: 初学者指南

在 Pandas 中重新排序列: 初学者指南

MoeNagy Dev

理解列顺序

列顺序在数据分析中的重要性

Pandas DataFrame 中列的顺序可能会对数据的呈现、分析和解释产生重大影响。保持一致且有意义的列顺序对于以下方面至关重要:

  • 提高数据结构的可读性和理解性
  • 促进数据探索和可视化
  • 确保与其他数据处理工具和库的兼容性
  • 实现高效直观的数据操作和分析

Pandas DataFrame 中的默认列顺序

在创建新的 Pandas DataFrame 时,默认列顺序由指定列的顺序或加载数据的顺序(例如,从 CSV 文件或数据库)决定。这个默认顺序可能无法满足您的分析需求,您可能需要重新排列列以满足特定要求。

使用列表重新排序列

指定列名列表

重新排序 Pandas DataFrame 中列的一种最简单的方法是提供一个包含列名的列表,按所需顺序排列。可以使用 df[column_list] 语法实现,其中 df 是 DataFrame,column_list 是列名列表。

import pandas as pd
 
# 创建一个示例 DataFrame
df = pd.DataFrame({
    'A': [1, 2, 3],
    'B': [4, 5, 6],
    'C': [7, 8, 9],
    'D': [10, 11, 12]
})
 
# 使用列表重新排序列
new_order = ['C', 'A', 'D', 'B']
df = df[new_order]

保留原始列顺序

如果您希望保留原始列顺序,同时只需要重新排序部分列,可以使用以下方法:这里是中文翻译:

# 在保留原始列顺序的同时重新排序列
new_order = ['C', 'A', 'D']
df = df.reindex(columns=new_order + [col for col in df.columns if col not in new_order])

处理列表中缺失的列

如果用于重新排序的列名列表包含在DataFrame中不存在的列,Pandas会优雅地处理这种情况,忽略缺失的列,只重新排序存在的列。

# 使用包含缺失列的列表重新排序列
new_order = ['C', 'A', 'D', 'E']
df = df[new_order]

在这种情况下,列'E'在DataFrame中不存在,所以它会被忽略,其余列会按照指定的顺序重新排序。

使用索引重新排序列

访问列索引

除了使用列名,您还可以通过指定列的索引位置来重新排序Pandas DataFrame中的列。您可以使用df.columns.tolist()df.columns.to_numpy()方法访问列索引。

# 访问列索引
column_indices = df.columns.tolist()

使用索引位置重新排序列

一旦您获得了列索引,就可以通过创建一个新的所需索引位置列表,并使用它来重新索引DataFrame来重新排序列。

# 使用索引位置重新排序列
new_order = [2, 0, 3, 1]
df = df.iloc[:, new_order]

反转列顺序

如果您想要反转DataFrame中列的顺序,可以使用[::-1]切片语法。

# 反转列顺序
df = df[df.columns[::-1]]

条件重排序

根据数据类型重新排序

您可以根据列的数据类型重新排序DataFrame中的列。当您想要将相关的列分组时,这可能很有用。根据数据类型重新排序列

# 根据数据类型重新排序列
dtypes = df.dtypes
numeric_cols = dtypes[dtypes == 'int64'].index.tolist()
categorical_cols = dtypes[dtypes == 'object'].index.tolist()
df = df[numeric_cols + categorical_cols]

在这个例子中,列被重新排序,使得所有数字列都放在分类列之前。

按数据类型分组列

您也可以按数据类型对列进行分组,并按特定顺序重新排序这些组。

# 按数据类型分组列并重新排序这些组
grouped_cols = df.dtypes.groupby(df.dtypes).groups
new_order = ['int64', 'float64', 'object']
df = df[sum([grouped_cols[t] for t in new_order], [])]

这种方法允许您控制列组的顺序,这对某些类型的分析或可视化可能很有用。

将特定列放在开头或结尾

如果您有特定的列希望始终放在DataFrame的开头或结尾,您可以使用前面提到的技术的组合。

# 将特定列放在开头或结尾
important_cols = ['A', 'D']
other_cols = [col for col in df.columns if col not in important_cols]
df = df[important_cols + other_cols]

在这个例子中,列'A'和'D'被放在DataFrame的开头,其余列跟在后面。

高级重排技术

组合重排方法

您可以结合前面讨论的不同重排技术,实现更复杂的列重排场景。

# 组合重排方法
numeric_cols = df.select_dtypes(include='int64').columns.tolist()
categorical_cols = df.select_dtypes(include='object').columns.tolist()
important_cols = ['A', 'D']
new_order = important_cols + numeric_cols + categorical_cols
df = df[new_order]

这个例子首先识别数字列和分类列,然后将重要列放在前面,其次是数字列,最后是分类列。基于列属性的重新排序

您还可以根据列的各种属性,如唯一值的数量、缺失值的百分比或列之间的相关性,来重新排序列。

# 根据唯一值的数量重新排序列
unique_counts = df.nunique()
new_order = unique_counts.sort_values().index.tolist()
df = df[new_order]

在这个例子中,列是根据每列的唯一值数量重新排序的,具有最少唯一值的列被放在最前面。

应用于DataFrame的子集

您还可以将重新排序技术应用于DataFrame的特定子集,如满足某些条件的行或列。

# 重新排序子集中的列
subset = df[df['A'] > 2]
subset = subset[['C', 'A', 'B']]

在这个例子中,根据条件 df['A'] > 2 创建了一个DataFrame子集,然后对子集中的列进行了重新排序。

性能优化

对于大型Pandas DataFrames,需要考虑重新排序列的性能影响。重新排序操作可能很耗计算资源,尤其是在处理非常宽或深的DataFrames时。

高效的重新排序策略

为了优化性能,您可以考虑以下策略:

  1. 使用就地重新排序: 不要创建新的DataFrame,而是使用 df.reindex(columns=new_order, inplace=True) 方法就地重新排序列。
  2. 避免不必要的计算: 如果您只需要重新排序部分列,请集中精力重新排序该子集,而不是整个DataFrame。
  3. 利用Pandas的内置方法: 尽可能使用Pandas的内置方法,如 df.reindex()df.iloc[],而不是手动创建.最小化不必要的计算

在重新排序列时,重要的是要最小化不必要的计算和内存使用。例如,如果您只需要重新排序一部分列,您可以避免为整个DataFrame创建新的DataFrame,而是专注于重新排序相关的子集。

# 重新排序一部分列
subset_cols = ['A', 'C', 'D']
df[subset_cols] = df[subset_cols].reindex(columns=new_order)

这种方法可能比为整个DataFrame创建新的DataFrame更有效。

保存重新排序的DataFrame

导出重新排序的DataFrame到文件

在Pandas DataFrame中重新排序列后,您可能希望将重新排序的DataFrame导出到文件,如CSV或Excel文件,以供进一步使用或共享。

# 将重新排序的DataFrame导出到CSV文件
df.to_csv('reordered_data.csv', index=False)

维护重新排序的状态以供将来使用

如果您需要在将来使用重新排序的DataFrame,您可以保存DataFrame的重新排序状态,方法是存储列顺序或保存整个DataFrame。

# 保存列顺序以供将来使用
column_order = df.columns.tolist()

然后,当您需要再次重新排序DataFrame时,您可以使用保存的列顺序:

# 使用保存的列顺序重新排序DataFrame
df = df[column_order]

这种方法在处理复杂的重新排序场景或需要维护DataFrame重新排序状态以实现可重复性或协作目的时特别有用。

实际示例和使用案例

为更好的可视化而重新排序列

重新排序列可以显著提高数据可视化(如条形图、散点图或热图)的可读性和清晰度。

# 为更好的可视化而重新排序列
import matplotlib.pyplot as plt
 
# 重新排序列
new_order = ['A', 'C', 'B', 'D']

df = df[new_order]

创建一个柱状图

df.plot(kind='bar') plt.show()


在这个例子中,列被重新排序以提供更直观和视觉上更吸引人的数据表示形式。

### 对齐合并或连接 DataFrames 的列

在使用多个 DataFrames 时,在执行合并或连接操作之前,确保列顺序对齐非常重要。重新排序列可以帮助防止错误,并确保数据正确组合。

```python
# 在合并 DataFrames 之前对列进行对齐
df1 = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})
df2 = pd.DataFrame({'B': [7, 8, 9], 'C': [10, 11, 12]})

# 重新排序列以对齐它们
df2 = df2[['B', 'C']]

# 合并 DataFrames
merged_df = pd.merge(df1, df2, on='B', how='inner')

在这个例子中,在执行合并操作之前,df2 中的列被重新排序以匹配 df1 中的列顺序。

针对特定分析优化列顺序

根据您正在执行的分析类型,最佳列顺序可能会有所不同。重新排序列可以帮助简化分析并提高数据处理工作流的整体效率。

# 针对特定分析优化列顺序
df = df[['A', 'C', 'B', 'D']]
 
# 在重新排序的 DataFrame 上执行分析
# ...

在这个例子中,列被重新排序以更好地适应正在执行的特定分析,这可以提高数据处理任务的可读性、可解释性和整体效率。

故障排除和常见陷阱

处理重新排序期间的错误

在重新排序列时,您可能会遇到各种错误,例如如果指定的列名不存在于 DataFrame 中,会出现 KeyError,或者如果提供的索引位置超出范围,会出现 IndexError

以下是将该代码翻译成中文的结果:

import pandas as pd
 
# 创建示例DataFrame
data = {'A': [1, 2, 3], 'B': [4, 5, 6], 'C': [7, 8, 9], 'D': [10, 11, 12]}
df = pd.DataFrame(data)
 
# 处理重新排序时的错误
try:
    # 使用列名重新排序列
    df = df[['A', 'B', 'C', 'D', 'E']] # 'E'列不存在,将引发KeyError
except KeyError as e:
    print(f"错误: 在DataFrame中未找到列 '{e.args[0]}'。") [1]
 
try:
    # 使用列索引重新排序列 
    df = df[[0, 1, 2, 3, 4]] # 索引4超出范围,将引发IndexError
except IndexError:
    print("错误: 一个或多个列索引超出范围。") [2]
 
print(df)

在这个示例中,我们首先创建了一个包含列'A'、'B'、'C'和'D'的示例DataFrame df

然后,我们使用两个try-except块来处理列重新排序时可能出现的错误:

  1. 在第一个try块中,我们尝试使用列名重新排序列。但是,我们包含了一个不存在的列'E',这将引发KeyError。在except块中,我们捕获KeyError并打印一条错误消息,指出哪一列未找到。我们为这一部分引用了相关的搜索结果[1]。

  2. 在第二个try块中,我们尝试使用列索引重新排序列。但是,我们包含了一个超出范围的索引(4),这将引发IndexError。在except块中,我们捕获IndexError并打印一条错误消息,指出一个或多个列索引超出范围。我们为这一部分引用了相关的搜索结果[2]。

最后,由于重新排序操作由于引入的错误而失败,我们打印原始的DataFrame df

通过优雅地处理这些错误,您可以为用户提供信息性错误消息,并防止程序意外崩溃。

定义一个类

在 Python 中,我们使用 class 关键字后跟类名来定义一个类。下面是一个简单的 Dog 类的示例:

class Dog:
    # 初始化对象的属性
    def __init__(self, name, breed):
        self.name = name
        self.breed = breed
 
    # 定义对象的方法
    def bark(self):
        print(f"{self.name} says: Woof!")

在这个例子中,Dog 类有两个属性(namebreed)和一个方法(bark())。__init__() 方法是一个特殊的方法,用于在创建对象时初始化对象的属性。

创建对象

要从一个类创建一个对象,我们使用类名作为一个函数,并将结果赋给一个变量。下面是一个例子:

my_dog = Dog("Buddy", "Labrador")
print(my_dog.name)  # 输出: Buddy
print(my_dog.breed)  # 输出: Labrador
my_dog.bark()  # 输出: Buddy says: Woof!

在这个例子中,我们创建了一个名为 my_dogDog 对象,它的名字是 "Buddy"、品种是 "Labrador"。然后我们访问对象的属性并调用它的 bark() 方法。

类属性和实例属性

除了实例属性(如 Dog 类中的 namebreed)之外,类还可以有类属性。类属性在类的所有实例之间共享,而实例属性是特定于每个对象的。

下面是一个包含类属性和实例属性的类的示例:

class Dog:
    # 类属性
    species = "Canis familiaris"
 
    # 初始化对象的属性
    def __init__(self, name, breed):
        self.name = name  # 实例属性
        self.breed = breed  # 实例属性
 
my_dog = Dog("Buddy", "Labrador")
print(my_dog.species)  # 输出: Canis familiaris
print(my_dog.name)  # 输出: Buddy
print(my_dog.breed)  # 输出: Labrador

在这个例子中,species 是一个类属性。

方法

方法是在类中定义的函数,用于操作对象的数据。有三种类型的方法:实例方法、类方法和静态方法。

实例方法: 实例方法可以访问对象的实例属性并修改它们。实例方法的第一个参数总是 self,它指的是当前类的实例。

class Dog:
    def __init__(self, name, breed):
        self.name = name
        self.breed = breed
 
    def bark(self):
        print(f"{self.name} says: Woof!")
 
my_dog = Dog("Buddy", "Labrador")
my_dog.bark()  # 输出: Buddy says: Woof!

类方法: 类方法可以访问类本身及其类属性。类方法的第一个参数总是 cls,它指的是类本身。

class Dog:
    species = "Canis familiaris"
 
    @classmethod
    def get_species(cls):
        return cls.species
 
print(Dog.get_species())  # 输出: Canis familiaris

静态方法: 静态方法是在类中定义的普通函数,它们无法访问对象的实例属性或类本身。它们通常用作工具函数。

class Math:
    @staticmethod
    def add(a, b):
        return a + b
 
result = Math.add(2, 3)
print(result)  # 输出: 5

继承

继承是面向对象编程的一个基本概念,它允许你基于现有的类创建新的类。新的类称为"派生"或"子"类,现有的类称为"基"或"父"类。

以下是一个继承自 Dog 类的 GoldenRetriever 类的示例:

class Dog:
    def __init__(self, name, breed):
        self.name = name
        self.breed = breed
 
    def bark(self):
        print(f"{self.name} says: Woof!")
 
class GoldenRetriever(Dog):
    def __init__(self, name):
        sup.
er().__init__(name, "金毛猎犬")
 
    def fetch(self):
        print(f"{self.name} 正在捡球!")
 
my_golden = GoldenRetriever("Buddy")
my_golden.bark()  # 输出: Buddy 说: 汪!
my_golden.fetch()  # 输出: Buddy 正在捡球!

在这个例子中, GoldenRetriever 类继承自 Dog 类。GoldenRetriever 类可以访问 Dog 类的所有属性和方法,并且它也可以定义自己的属性和方法,比如 fetch() 方法。

多态

多态是指不同类的对象可以被视为一个共同的父类对象的能力。这允许您编写更通用和可重用的代码。

下面是一个使用 DogGoldenRetriever 类的多态示例:

class Dog:
    def __init__(self, name, breed):
        self.name = name
        self.breed = breed
 
    def make_sound(self):
        print(f"{self.name} 说: 汪!")
 
class GoldenRetriever(Dog):
    def make_sound(self):
        print(f"{self.name} 说: 吠!")
 
def call_animal(animal):
    animal.make_sound()
 
my_dog = Dog("Buddy", "拉布拉多")
my_golden = GoldenRetriever("Buddy")
 
call_animal(my_dog)  # 输出: Buddy 说: 汪!
call_animal(my_golden)  # 输出: Buddy 说: 吠!

在这个例子中, call_animal() 函数可以接受 DogGoldenRetriever 对象,并且它将调用每个对象的适当 make_sound() 方法,尽管它们有不同的实现。

异常

异常是程序执行过程中发生的事件,会中断程序的正常执行流程。Python 有一个内置的异常处理机制,允许您处理和管理这些异常。

下面是一个处理 ZeroDivisionError 异常的示例:

try:
    result = 10 / 0
except ZeroDivisionError:
    print("错误: 除数为零")
else:
    print(f"结果: {result}")
finally:
    print("操作已完成")
# 异常处理
try:
    result = 10 / 0  # 尝试将 10 除以 0,这将引发 ZeroDivisionError 异常
except ZeroDivisionError:
    print("错误: 不能将数字除以 0")  # 捕获并处理 ZeroDivisionError 异常
else:
    print("没有发生异常,结果为:", result)  # 如果没有发生异常,则执行此代码块
finally:
    print("无论是否发生异常,都会执行此代码块")  # 无论是否发生异常,都会执行此代码块

在这个示例中,try 块尝试将 10 除以 0,这将引发 ZeroDivisionError 异常。except 块捕获该异常并打印错误消息。else 块在没有发生异常时执行,finally 块无论是否发生异常都会执行。

您也可以定义自己的自定义异常,方法是创建一个继承自 Exception 类或其子类的新类。

模块和包

Python 中,模块是包含代码的单个 Python 文件,包是相关模块的集合。模块和包有助于组织您的代码并提高其可重用性。

# math_utils.py
def add(a, b):
    return a + b
 
def subtract(a, b):
    return a - b
 
# main.py
from math_utils import add, subtract
 
result_add = add(2, 3)
result_subtract = subtract(5, 3)
 
print(f"加法结果: {result_add}")
print(f"减法结果: {result_subtract}")

在这个示例中,我们创建了一个名为 math_utils.py 的模块,其中包含两个函数 add()subtract()。在 main.py 脚本中,我们从 math_utils 模块导入这些函数并使用它们。

包是通过在包含相关模块的目录中添加 __init__.py 文件来创建的。这允许您将代码组织成分层结构,并从包中导入模块。

结论

在本教程中,您学习了 Python 中面向对象编程的基本概念,包括类、对象、继承、多态和异常处理。您还探索了模块和包,它们有助于组织和重用您的代码。

这些概念对于构建复杂和可维护的 Python 应用程序至关重要。通过掌握这些主题,您将成为一名熟练的 Python 程序员。

MoeNagy Dev