Python datatable/pandas 整形问题
Python datatable/pandas reshaping problem
我需要重塑我的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()
转型:
- 这是分组变换。分组列为 'name'.
- df 已经排序
- 每组行数不同,可偶数也可奇数
- 如果组中的第一行在 'type' 列中有一个 'b',则必须将其删除(例如:DF_in 中的第 4 行)
- 也有可能一组中的最后一行在'type'列中有一个'a',这一行应该不会丢失(例如:DF_in中的第7行)
我希望这个解释是可以理解的。
提前致谢
让我们使用数据帧,所以先加载数据
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
s
- 在'g'
列分配组号
- 通过
set_index
+ unstack
调整 table
- 将列重命名为所需格式
- 删除不需要的列
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
datatable 没有允许在垂直和水平位置之间翻转的整形功能;因此,pandas 是您最好的选择。
以下是我对你的挑战的尝试:
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 列
其余部分依赖于枢轴和一些名称编辑(set_axis 和重命名功能)。您可以使用 pivot_wider function from pyjanitor:
进一步抽象
# 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')
)
非常感谢大家的回答。与此同时,我开发了一个仅使用数据表包的解决方案,针对当前的限制使用了一些解决方法:
- 定义一个函数来为相邻行创建 id:1,1,2,2,...
- 创建包含行索引的列 ID
- 获取要删除的行的 id 作为列表
- 从所有行 ID 中减去要删除的行 ID
- 根据剩余的行 ID 对框架进行子集化
- 获取每组的行数
- 对每个组使用该函数并使用行数作为输入,
创建一个包含所有结果的列表(与子集后的帧长度相同)。将其绑定到框架
- 根据列类型('a' 或 'b')创建两个子集 Frames
- 在 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
我需要重塑我的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()
转型:
- 这是分组变换。分组列为 'name'.
- df 已经排序
- 每组行数不同,可偶数也可奇数
- 如果组中的第一行在 'type' 列中有一个 'b',则必须将其删除(例如:DF_in 中的第 4 行)
- 也有可能一组中的最后一行在'type'列中有一个'a',这一行应该不会丢失(例如:DF_in中的第7行)
我希望这个解释是可以理解的。
提前致谢
让我们使用数据帧,所以先加载数据
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
s - 在'g' 列分配组号
- 通过
set_index
+unstack
调整 table
- 将列重命名为所需格式
- 删除不需要的列
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
datatable 没有允许在垂直和水平位置之间翻转的整形功能;因此,pandas 是您最好的选择。
以下是我对你的挑战的尝试:
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 列
其余部分依赖于枢轴和一些名称编辑(set_axis 和重命名功能)。您可以使用 pivot_wider function from pyjanitor:
进一步抽象# 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') )
非常感谢大家的回答。与此同时,我开发了一个仅使用数据表包的解决方案,针对当前的限制使用了一些解决方法:
- 定义一个函数来为相邻行创建 id:1,1,2,2,...
- 创建包含行索引的列 ID
- 获取要删除的行的 id 作为列表
- 从所有行 ID 中减去要删除的行 ID
- 根据剩余的行 ID 对框架进行子集化
- 获取每组的行数
- 对每个组使用该函数并使用行数作为输入, 创建一个包含所有结果的列表(与子集后的帧长度相同)。将其绑定到框架
- 根据列类型('a' 或 'b')创建两个子集 Frames
- 在 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