我有一个熊猫数据帧,df:
c1 c2
0 10 100
1 11 110
2 12 120
如何迭代此数据帧的行?对于每一行,我希望能够通过列的名称访问其元素(单元格中的值)。例如:
for row in df.rows:
print(row['c1'], row['c2'])
我发现了一个类似的问题,建议使用以下任一项:
for date, row in df.T.iteritems():
for row in df.iterrows():
但我不知道row对象是什么,以及如何使用它。
首先考虑是否真的需要迭代DataFrame中的行。请参阅此答案以了解备选方案。
如果仍然需要迭代行,可以使用以下方法。请注意其他答案中未提及的一些重要注意事项。
DataFrame.iterrows()对于索引,df.iterrows()中的行:打印(行[“c1”],行[“c2”])DataFrame.itertuples()对于df.itertuples中的行(索引=True,名称=“标准”):打印(第c1行,第c2行)
itertples()应该比iterrows()快
但请注意,根据文件(熊猫目前为0.24.2):
iterrows:dtype可能在行与行之间不匹配
因为iterrows为每一行返回一个Series,所以它不会跨行保留数据类型(数据帧的数据类型跨列保留)。为了在遍历行时保留数据类型,最好使用itertples(),它返回值的namedtuples,通常比iterrows()快得多
iterrows:不修改行
您不应该修改正在迭代的内容。这并不能保证在所有情况下都有效。根据数据类型的不同,迭代器返回的是副本而不是视图,写入它不会产生任何影响。
请改用DataFrame.apply():
new_df = df.apply(lambda x: x * 2, axis = 1)
迭代:
如果列名是无效的Python标识符、重复或以下划线开头,则将重命名为位置名。对于大量列(>255),将返回常规元组。
有关详细信息,请参阅panda迭代文档。
有些库(例如我使用的Java互操作库)要求一次在一行中传递值,例如,如果是流数据。为了复制流式传输的特性,我将数据帧值逐一“流式传输”,我写了以下内容,这些内容不时会派上用场。
class DataFrameReader:
def __init__(self, df):
self._df = df
self._row = None
self._columns = df.columns.tolist()
self.reset()
self.row_index = 0
def __getattr__(self, key):
return self.__getitem__(key)
def read(self) -> bool:
self._row = next(self._iterator, None)
self.row_index += 1
return self._row is not None
def columns(self):
return self._columns
def reset(self) -> None:
self._iterator = self._df.itertuples()
def get_index(self):
return self._row[0]
def index(self):
return self._row[0]
def to_dict(self, columns: List[str] = None):
return self.row(columns=columns)
def tolist(self, cols) -> List[object]:
return [self.__getitem__(c) for c in cols]
def row(self, columns: List[str] = None) -> Dict[str, object]:
cols = set(self._columns if columns is None else columns)
return {c : self.__getitem__(c) for c in self._columns if c in cols}
def __getitem__(self, key) -> object:
# the df index of the row is at index 0
try:
if type(key) is list:
ix = [self._columns.index(key) + 1 for k in key]
else:
ix = self._columns.index(key) + 1
return self._row[ix]
except BaseException as e:
return None
def __next__(self) -> 'DataFrameReader':
if self.read():
return self
else:
raise StopIteration
def __iter__(self) -> 'DataFrameReader':
return self
可用于:
for row in DataFrameReader(df):
print(row.my_column_name)
print(row.to_dict())
print(row['my_column_name'])
print(row.tolist())
并保留正在迭代的行的值/名称映射。显然,它比上面提到的使用apply和Cython慢得多,但在某些情况下是必要的。
在Pandas数据帧中有很多方法可以迭代行。一种非常简单直观的方法是:
df = pd.DataFrame({'A':[1, 2, 3], 'B':[4, 5, 6], 'C':[7, 8, 9]})
print(df)
for i in range(df.shape[0]):
# For printing the second column
print(df.iloc[i, 1])
# For printing more than one columns
print(df.iloc[i, [0, 2]])
免责声明:尽管这里有很多答案建议不要使用迭代(循环)方法(我基本同意),但我仍然认为这是一种适用于以下情况的合理方法:
使用API中的数据扩展数据帧
假设您有一个包含不完整用户数据的大型数据帧。现在,您必须使用其他列来扩展此数据,例如,用户的年龄和性别。
这两个值都必须从后端API获取。我假设API不提供“批处理”端点(一次接受多个用户ID)。否则,您应该只调用一次API。
网络请求的成本(等待时间)远远超过了数据帧的迭代。我们讨论的是数百毫秒的网络往返时间,相比之下,使用迭代的替代方法可以忽略不计的小增益。
每行一个昂贵的网络请求
所以在这种情况下,我绝对倾向于使用迭代方法。尽管网络请求很昂贵,但可以保证对数据帧中的每一行只触发一次。以下是使用DataFrame.iterrows的示例:
实例
for index, row in users_df.iterrows():
user_id = row['user_id']
# Trigger expensive network request once for each row
response_dict = backend_api.get(f'/api/user-data/{user_id}')
# Extend dataframe with multiple data from response
users_df.at[index, 'age'] = response_dict.get('age')
users_df.at[index, 'gender'] = response_dict.get('gender')