在 DataFrame 中相对于列重复行

Ern*_*niu 9 python dataframe pandas

我有一个 Pandas DataFrame,如下所示:

df = pd.DataFrame({'col1': [1, 2, 3],
                   'col2': [4, 5, 6],
                   'col3': [7, 8, 9]})

df
    col1    col2    col3
0      1       4       7
1      2       5       8
2      3       6       9
Run Code Online (Sandbox Code Playgroud)

我想创建一个像这样的 Pandas DataFrame:

df_new
    col1    col2    col3
0      1       4       7
1      1       5       8
2      1       6       9
3      2       4       7
4      2       5       8
5      2       6       9
6      3       4       7
7      3       5       8
8      3       6       9
Run Code Online (Sandbox Code Playgroud)

是否有内置或内置 Pandas 方法的组合可以实现此目的?

即使 中存在重复项df,我也希望输出具有相同的格式。换句话说:

df
    col1    col2    col3
0      1       4       7
1      2       5       8
2      2       6       8

df_new
    col1    col2    col3
0      1       4       7
1      1       5       8
2      1       6       8
3      2       4       7
4      2       5       8
5      2       6       8
6      2       4       7
7      2       5       8
8      2       6       8
Run Code Online (Sandbox Code Playgroud)

rr_*_*yal 8

我很想看到一个更加Pythonic或“熊猫独有”的答案,但是这个也很好用!

import pandas as pd
import numpy as np

n=3

df = pd.DataFrame({'col1': [1, 2, 3],
                   'col2': [4, 5, 6],
                   'col3': [7, 8, 9]})

# Edited and added this new method.
df2 = pd.DataFrame({df.columns[0]:np.repeat(df['col1'].values, n)})
df2[df.columns[1:]] = df.iloc[:,1:].apply(lambda x: np.tile(x, n))

""" Old method.
for col in df.columns[1:]:
   df2[col] = np.tile(df[col].values, n)

"""
print(df2)
Run Code Online (Sandbox Code Playgroud)


moz*_*way 7

我也会merge按照 @Henry 在评论中的建议选择十字架:

out = df[['col1']].merge(df[['col2', 'col3']], how='cross').reset_index(drop=True)
Run Code Online (Sandbox Code Playgroud)

输出:

   col1  col2  col3
0     1     4     7
1     1     5     8
2     1     6     9
3     2     4     7
4     2     5     8
5     2     6     9
6     3     4     7
7     3     5     8
8     3     6     9
Run Code Online (Sandbox Code Playgroud)

不同方法的比较:

在此输入图像描述

请注意,当行重复时,@sammywemmy 的方法表现不同,这会导致不可比较的计时。

  • @mozway 很酷的东西。有趣的是,itertools 是最慢的选项。我喜欢能得到如此多样化的回答的问题,也喜欢有人负责计时。 (4认同)
  • @sammywemmy关于您的方法,当存在重复的行时,它不提供与其他方法相同的输出(因此时间不同) (2认同)
  • 很高兴学习各种新方法! (2认同)

Nic*_*ick 6

您可以连接数据帧的副本,并col1在每个副本中替换为 中的每个值col1

out = df.drop('col1', axis=1)
out = pd.concat([out.assign(col1=c1) for c1 in df['col1']]).reset_index(drop=True)
Run Code Online (Sandbox Code Playgroud)

输出:

   col2  col3  col1
0     4     7     1
1     5     8     1
2     6     9     1
3     4     7     2
4     5     8     2
5     6     9     2
6     4     7     3
7     5     8     3
8     6     9     3
Run Code Online (Sandbox Code Playgroud)

如果您愿意,您可以使用以下命令将列重新排序回原始状态

   col2  col3  col1
0     4     7     1
1     5     8     1
2     6     9     1
3     4     7     2
4     5     8     2
5     6     9     2
6     4     7     3
7     5     8     3
8     6     9     3
Run Code Online (Sandbox Code Playgroud)


Cor*_*ien 6

您可以使用np.repeatnp.tile来获得预期的输出:

import numpy as np

N = 3
cols_to_repeat = ['col1']  # 1, 1, 1, 2, 2, 2
cols_to_tile = ['col2', 'col3']  # 1, 2, 1, 2, 1, 2

data = np.concatenate([np.tile(df[cols_to_tile].values.T, N).T,
                       np.repeat(df[cols_to_repeat].values, N, axis=0)], axis=1)
out = pd.DataFrame(data, columns=cols_to_tile + cols_to_repeat)[df.columns]
Run Code Online (Sandbox Code Playgroud)

输出:

>>> out
   col1  col2  col3
0     1     4     7
1     1     5     8
2     1     6     9
3     2     4     7
4     2     5     8
5     2     6     9
6     3     4     7
7     3     5     8
8     3     6     9
Run Code Online (Sandbox Code Playgroud)

您可以创建一个通用函数:

def repeat(df: pd.DataFrame, to_repeat: list[str], to_tile: list[str]=None) -> pd.DataFrame:
    to_tile = to_tile if to_tile else df.columns.difference(to_repeat).tolist()

    assert df.columns.difference(to_repeat + to_tile).empty, "all columns should be repeated or tiled"

    data = np.concatenate([np.tile(df[to_tile].values.T, N).T,
                           np.repeat(df[to_repeat].values, N, axis=0)], axis=1)

    return pd.DataFrame(data, columns=to_tile + to_repeat)[df.columns]

repeat(df, ['col1'])
Run Code Online (Sandbox Code Playgroud)

用法:

>>> repeat(df, ['col1'])
   col1  col2  col3
0     1     4     7
1     1     5     8
2     1     6     9
3     2     4     7
4     2     5     8
5     2     6     9
6     3     4     7
7     3     5     8
8     3     6     9
Run Code Online (Sandbox Code Playgroud)


Pau*_*ulS 6

另一种可能的解决方案基于itertools.product

from itertools import product

pd.DataFrame([[x, y[0], y[1]] for x, y in 
              product(df['col1'], zip(df['col2'], df['col3']))], 
             columns=df.columns)
Run Code Online (Sandbox Code Playgroud)

输出:

   col1  col2  col3
0     1     4     7
1     1     5     8
2     1     6     9
3     2     4     7
4     2     5     8
5     2     6     9
6     3     4     7
7     3     5     8
8     3     6     9
Run Code Online (Sandbox Code Playgroud)


sam*_*mmy 6

一种选择是从pyjanitor完成

# pip install pyjanitor
import janitor 
import pandas as pd

df.complete('col1', ('col2','col3'))
   col1  col2  col3
0     1     4     7
1     1     5     8
2     1     6     9
3     2     4     7
4     2     5     8
5     2     6     9
6     3     4     7
7     3     5     8
8     3     6     9
Run Code Online (Sandbox Code Playgroud)

完整主要是为了暴露丢失的行 - 上面的输出恰好是一个很好的副作用。一个更合适但相当冗长的选项是Expand_grid

# pip install pyjanitor
import janitor as jn
import pandas as pd

others = {'df1':df.col1, 'df2':df[['col2','col3']]}
jn.expand_grid(others=others).droplevel(axis=1,level=0)
   col1  col2  col3
0     1     4     7
1     1     5     8
2     1     6     8
3     2     4     7
4     2     5     8
5     2     6     8
6     2     4     7
7     2     5     8
8     2     6     8
Run Code Online (Sandbox Code Playgroud)

  • 对于非重复的情况来说,它很好而且简短,但不幸的是不能正确处理其他情况。 (3认同)