1

我需要重塑我的df。

这是我的输入 df:

import pandas as pd
import datatable as dt

DF_in = dt.Frame(name=['name1', 'name1', 'name1', 'name1', 'name2', 'name2', 'name2', 'name2'],
             date=['2021-01-01', '2021-01-02', '2021-01-03', '2021-01-04', '2021-01-05', '2021-01-06', '2021-01-07', '2021-01-08'],
             type=['a', 'b', 'a', 'b', 'b', 'a', 'b', 'a'],
             value=[1, 2, 3, 4, 5, 6, 7, 8])

   | name   date        type  value
-- + -----  ----------  ----  -----
 0 | name1  2021-01-01  a         1
 1 | name1  2021-01-02  b         2
 2 | name1  2021-01-03  a         3
 3 | name1  2021-01-04  b         4
 4 | name2  2021-01-05  b         5
 5 | name2  2021-01-06  a         6
 6 | name2  2021-01-07  b         7
 7 | name2  2021-01-08  a         8

这是所需的输出 df:

DF_out = dt.Frame(name=['name1', 'name1', 'name2', 'name2'],
              date_a=['2021-01-01', '2021-01-03', '2021-01-06', '2021-01-08'],
              date_b=['2021-01-02', '2021-01-04', '2021-01-07', None],
              value_a=[1, 3, 6, 8],
              value_b=[2, 4, 7, None])

   | name   date_a      date_b      value_a  value_b
-- + -----  ----------  ----------  -------  -------
 0 | name1  2021-01-01  2021-01-02        1        2
 1 | name1  2021-01-03  2021-01-04        3        4
 2 | name2  2021-01-06  2021-01-07        6        7
 3 | name2  2021-01-08  NA                8       NA

如有必要,可以将数据表 Frames 转换为 pandas DataFrame:

DF_in = DF_in.to_pandas()

转型:

  • 这是一个分组转换。分组列是“名称”。
  • df 已经排序
  • 每组的行数不同,可以是偶数也可以是不偶数
  • 如果组中的第一行在“类型”列中有“b”,则必须将其删除(例如:DF_in 中的第 4 行)
  • 也有可能组中的最后一行在“type”列中有一个“a”,这一行不应丢失(例如:DF_in 中的第 7 行)

我希望这个解释是可以理解的。

先感谢您

4

3 回答 3

2

非常感谢大家的回答。与此同时,我开发了一个仅使用数据表包的解决方案,针对当前限制使用了一些解决方法:

  1. 定义一个函数来为相邻行创建 id:1,1,2,2,...
  2. 创建包含行索引的列 id
  3. 获取要删除的行的 ID 作为列表
  4. 从所有行 ID 中减去要删除的行 ID
  5. 根据剩余的行 ID 对 Frame 进行子集化
  6. 获取每组的行数
  7. 对每个组使用该函数并使用行数作为输入,创建一个包含所有结果的列表(与子集后的帧长度相同)。将此绑定到框架
  8. 根据列类型(“a”或“b”)创建两个子框架
  9. 在 df1 上加入 df2

代码:

import math
import datatable as dt
from datatable import dt, f, by, update, join

DF_in = dt.Frame(name=['name1', 'name1', 'name1', 'name1', 'name2', 'name2', 'name2', 'name2'],
                 date=['2021-01-01', '2021-01-02', '2021-01-03', '2021-01-04', '2021-01-05', '2021-01-06', '2021-01-07', '2021-01-08'],
                 type=['a', 'b', 'a', 'b', 'b', 'a', 'b', 'a'],
                 value=[1, 2, 3, 4, 5, 6, 7, 8])



def group_id(n):
    l = [x for x in range(0, math.floor(n / 2))]
    l = sorted(l * 2)
    if n % 2 != 0:
        try:
            l.append(l[-1] + 1)
        except IndexError:
            l.append(0)
    return l


DF_in['id'] = range(DF_in.nrows)
first_row = f.id==dt.min(f.id)
row_eq_b = dt.first(f.type)=="b"
remove_rows = first_row & row_eq_b
DF_in[:, update(remove_rows = ~remove_rows), 'name']
DF_in = DF_in[f[-1]==1, :-1]
group_count = DF_in[:, {"Count": dt.count()}, by('name')][:, 'Count'].to_list()[0]
group_id_column = []

for x in group_count:
    group_id_column = group_id_column + group_id(x)

DF_in['group_id'] = dt.Frame(group_id_column)
df1 = DF_in[f.type == 'a', ['name', 'date', 'value', 'group_id']]
df2 = DF_in[f.type == 'b', ['name', 'date', 'value', 'group_id']]

df2.key = ['name', 'group_id']
DF_out = df1[:, :, join(df2)]
DF_out.names = {'date': 'date_a', 'value': 'value_a', 'date.0': 'date_b', 'value.0': 'value_b'}

DF_out[:, ['name', 'date_a', 'date_b', 'value_a', 'value_b']]

   | name   date_a      date_b      value_a  value_b
-- + -----  ----------  ----------  -------  -------
 0 | name1  2021-01-01  2021-01-02        1        2
 1 | name1  2021-01-03  2021-01-04        3        4
 2 | name2  2021-01-06  2021-01-07        6        7
 3 | name2  2021-01-08  NA                8       NA
于 2021-04-04T11:49:34.353 回答
1

让我们使用数据框,所以首先加载数据

df = pd.DataFrame(dict(name=['name1', 'name1', 'name1', 'name1', 'name2', 'name2', 'name2', 'name2'],
             date=['2021-01-01', '2021-01-02', '2021-01-03', '2021-01-04', '2021-01-05', '2021-01-06', '2021-01-07', '2021-01-08'],
             type=['a', 'b', 'a', 'b', 'b', 'a', 'b', 'a'],
             value=[1, 2, 3, 4, 5, 6, 7, 8]))

然后在下面我们执行以下步骤

  • 摆脱b
  • 在“g”列中分配组号
  • set_index通过+旋转表格unstack
  • 将列重命名为所需的格式
  • 删除不需要的列
df1 = df[~((df['type'] == 'b') & (df['type'].shift() == 'b'))].copy()
df1['g'] = np.arange(len(df1))//2
df2 = df1.set_index(['g','type']).unstack(level=1)
df2.columns = ['_'.join(tup).rstrip('_') for tup in df2.columns.values]
df2.drop(columns = 'name_b').rename(columns = {'name_a':'name'})

输出

    name    date_a      date_b      value_a value_b
g                   
0   name1   2021-01-01  2021-01-02  1.0     2.0
1   name1   2021-01-03  2021-01-04  3.0     4.0
2   name2   2021-01-06  2021-01-07  6.0     7.0
3   name2   2021-01-08  NaN         8.0     NaN
于 2021-04-03T17:41:31.070 回答
1

数据表没有允许在垂直和水平位置之间翻转的重塑功能;因此,熊猫是您最好的选择。

以下是我对您的挑战的尝试:

    from datatable import dt
    import pandas as pd

    df = DF_in.to_pandas()

    (df
     .assign(temp = df.index, # needed for ranking
             b_first = lambda df: df.groupby('name')['type'].transform('first'))
     .assign(temp = lambda df: df.groupby('name')['temp'].rank())
      # get rid of rows in groups where b is first
     .query('~(temp==1 and b_first=="b")')
      # needed to get unique values in index when pivoting
     .assign(temp = lambda df: df.groupby(['name','type']).cumcount())
     .pivot(['name','temp'], ['type'], ['date','value'])
     .pipe(lambda df: df.set_axis(df.columns.to_flat_index(), axis='columns')
     .rename(columns = lambda df: "_".join(df)))
     .droplevel('temp')
     .reset_index()
      )

    name      date_a      date_b value_a value_b
0  name1  2021-01-01  2021-01-02       1       2
1  name1  2021-01-03  2021-01-04       3       4
2  name2  2021-01-06  2021-01-07       6       7
3  name2  2021-01-08         NaN       8     NaN

概括:

  • 过滤掉“b”是组中第一个条目的行

  • 为避免在旋转(重新索引)时由于重复索引而导致错误,请创建一个临时 cumcount 列

  • 其余的依赖于 pivot 和一些名称编辑(set_axis 和 rename 函数)。您可以使用 pyjanitor 的pivot_wider函数进一步抽象一点:

     # pip install pyjanitor
     import janitor
    
     (df
     .assign(temp = df.index, 
             b_first = lambda df: df.groupby('name')['type'].transform('first'))
     .assign(temp = lambda df: df.groupby('name')['temp'].rank())
     .query('~(temp==1 and b_first=="b")')
     .assign(temp = lambda df: df.groupby(['name','type']).cumcount())
     .pivot_wider(index=['name', 'temp'], 
                  names_from=['type'], 
                  values_from=['date','value'],   
                  names_sep="_",
                  names_from_position='last')
     .drop(columns='temp')
      )
    
于 2021-04-03T23:41:05.620 回答