将值重新格式化为单独的列
Reformat values into separate columns
我正在尝试将值分隔到 pandas df 中的不同列中。具体来说,我在同一列中有代表标签和时间戳的字符串。我希望将它们分成单独的列。我只是不确定更有效的过程是什么。
对于下面的 df,我想将时间字符串分隔到一个单独的列中。
df = pd.DataFrame({
'Value' : ['Foo X','10:00','10:00','10:00','10:00','Bar X','11:00','11:00','Cat X','12:00','12:00','12:00'],
'Number' : [0,1,2,3,4,0,1,2,0,1,2,3],
})
输出:
Value Number
0 Foo X 0
1 10:00 1
2 10:00 2
3 10:00 3
4 10:00 4
5 Bar X 0
6 11:00 1
7 11:00 2
8 Cat X 0
9 12:00 1
10 12:00 2
11 12:00 3
问题是每个标签的时间戳数量不同,所以我不能只拆分每第 n 行。例如
df1 = pd.DataFrame({'Value':df['Value'].iloc[:1:4].values, 'Time':df['Value'].iloc[:1:4].values})
另一种尝试可能是创建一个单独的列来传递来自 df.Value 的所有值,然后用 np.nan 替换所有时间戳并对输出进行子集化。但是我不确定这是否非常有效?
Value Number Time
0 Foo X 0 Foo X
1 10:00 1 10:00
2 10:00 2 10:00
3 10:00 3 10:00
4 10:00 4 10:00
5 Bar X 0 Bar X
6 11:00 1 11:00
7 11:00 2 11:00
8 Cat X 0 Cat X
9 12:00 1 12:00
10 12:00 2 12:00
11 12:00 3 12:00
预期输出:
Value Number Time
0 Foo X 1 10:00
1 Foo X 2 10:00
2 Foo X 3 10:00
3 Foo X 4 10:00
4 Bar X 1 11:00
5 Bar X 2 11:00
6 Cat X 1 12:00
7 Cat X 2 12:00
8 Cat X 3 12:00
下面的函数应该会给你想要的输出。
def process_dataframe(df):
s = df.loc[df.Number==0]['Value']
labels = s.to_list()
a = s.index.to_list()
a.append(df.index.size)
repnum = [x2 - x1 - 1 for x1,x2 in zip(a[:-1], a[1:])]
df2 = df.loc[df['Number']!=0].copy()
df2['Time'] = df2['Value']
df2['Value'] = s.repeat(repnum).to_list()
return df2
process_dataframe(df)
Output
Value Number Time
0 Foo X 1 10:00
1 Foo X 2 10:00
2 Foo X 3 10:00
3 Foo X 4 10:00
4 Bar X 1 11:00
5 Bar X 2 11:00
6 Cat X 1 12:00
7 Cat X 2 12:00
8 Cat X 3 12:00
您可以使用 groupby with pd.Series.repat
创建 Value
列
然后 select Time
和 Number
通过使用 boolean indexing:
value_bool=pd.Series(['X' in key for key in df['Value']])
Value=df.loc[value_bool]['Value'] #selecting values for the Value column
groups=df.groupby(value_bool.cumsum())
new_df=Value.repeat(groups.size()-1).to_frame().reset_index(drop=True) #create dataframe with new Value Column
new_df[['Number','Time']]=df.loc[~value_bool].reset_index(drop=True).reindex(columns=['Number','Value']) #creating Number and Time
输出:
Value Number Time
0 Foo X 1 10:00
1 Foo X 2 10:00
2 Foo X 3 10:00
3 Foo X 4 10:00
4 Bar X 1 11:00
5 Bar X 2 11:00
6 Cat X 1 12:00
7 Cat X 2 12:00
8 Cat X 3 12:00
想法是通过 to_datetime
和 errors='coerce'
来区分时间值,用于缺少不匹配的值:
mask = pd.to_datetime(df['Value'], errors='coerce').notna()
或 Series.str.contains
用于测试模式 2 位数字 :
:
mask = df['Value'].str.contains(r'\d{2}:\d{2}')
或 id 可能测试不等于 0
:
mask = df['Number'].ne(0)
然后创建新列并将 Value
替换为 NaN
s by mask
with Series.mask
and forward filling missing values, last filter by boolean indexing
:
df['Time'] = df['Value']
df['Value'] = df['Value'].mask(mask).ffill()
df = df[mask].copy()
print (df)
Value Number Time
1 Foo X 1 10:00
2 Foo X 2 10:00
3 Foo X 3 10:00
4 Foo X 4 10:00
6 Bar X 1 11:00
7 Bar X 2 11:00
9 Cat X 1 12:00
10 Cat X 2 12:00
11 Cat X 3 12:00
另一种使用遮罩和填充的解决方案:
(
df.assign(ind=df.Value.mask(df.Value.str.contains('^\d+:\d+')).ffill())
.loc[lambda x: x.Number.ne(0)]
.set_axis(['Time','Number', 'Value'], axis=1, inplace=False)
)
我正在尝试将值分隔到 pandas df 中的不同列中。具体来说,我在同一列中有代表标签和时间戳的字符串。我希望将它们分成单独的列。我只是不确定更有效的过程是什么。
对于下面的 df,我想将时间字符串分隔到一个单独的列中。
df = pd.DataFrame({
'Value' : ['Foo X','10:00','10:00','10:00','10:00','Bar X','11:00','11:00','Cat X','12:00','12:00','12:00'],
'Number' : [0,1,2,3,4,0,1,2,0,1,2,3],
})
输出:
Value Number
0 Foo X 0
1 10:00 1
2 10:00 2
3 10:00 3
4 10:00 4
5 Bar X 0
6 11:00 1
7 11:00 2
8 Cat X 0
9 12:00 1
10 12:00 2
11 12:00 3
问题是每个标签的时间戳数量不同,所以我不能只拆分每第 n 行。例如
df1 = pd.DataFrame({'Value':df['Value'].iloc[:1:4].values, 'Time':df['Value'].iloc[:1:4].values})
另一种尝试可能是创建一个单独的列来传递来自 df.Value 的所有值,然后用 np.nan 替换所有时间戳并对输出进行子集化。但是我不确定这是否非常有效?
Value Number Time
0 Foo X 0 Foo X
1 10:00 1 10:00
2 10:00 2 10:00
3 10:00 3 10:00
4 10:00 4 10:00
5 Bar X 0 Bar X
6 11:00 1 11:00
7 11:00 2 11:00
8 Cat X 0 Cat X
9 12:00 1 12:00
10 12:00 2 12:00
11 12:00 3 12:00
预期输出:
Value Number Time
0 Foo X 1 10:00
1 Foo X 2 10:00
2 Foo X 3 10:00
3 Foo X 4 10:00
4 Bar X 1 11:00
5 Bar X 2 11:00
6 Cat X 1 12:00
7 Cat X 2 12:00
8 Cat X 3 12:00
下面的函数应该会给你想要的输出。
def process_dataframe(df):
s = df.loc[df.Number==0]['Value']
labels = s.to_list()
a = s.index.to_list()
a.append(df.index.size)
repnum = [x2 - x1 - 1 for x1,x2 in zip(a[:-1], a[1:])]
df2 = df.loc[df['Number']!=0].copy()
df2['Time'] = df2['Value']
df2['Value'] = s.repeat(repnum).to_list()
return df2
process_dataframe(df)
Output
Value Number Time
0 Foo X 1 10:00
1 Foo X 2 10:00
2 Foo X 3 10:00
3 Foo X 4 10:00
4 Bar X 1 11:00
5 Bar X 2 11:00
6 Cat X 1 12:00
7 Cat X 2 12:00
8 Cat X 3 12:00
您可以使用 groupby with pd.Series.repat
创建 Value
列
然后 select Time
和 Number
通过使用 boolean indexing:
value_bool=pd.Series(['X' in key for key in df['Value']])
Value=df.loc[value_bool]['Value'] #selecting values for the Value column
groups=df.groupby(value_bool.cumsum())
new_df=Value.repeat(groups.size()-1).to_frame().reset_index(drop=True) #create dataframe with new Value Column
new_df[['Number','Time']]=df.loc[~value_bool].reset_index(drop=True).reindex(columns=['Number','Value']) #creating Number and Time
输出:
Value Number Time
0 Foo X 1 10:00
1 Foo X 2 10:00
2 Foo X 3 10:00
3 Foo X 4 10:00
4 Bar X 1 11:00
5 Bar X 2 11:00
6 Cat X 1 12:00
7 Cat X 2 12:00
8 Cat X 3 12:00
想法是通过 to_datetime
和 errors='coerce'
来区分时间值,用于缺少不匹配的值:
mask = pd.to_datetime(df['Value'], errors='coerce').notna()
或 Series.str.contains
用于测试模式 2 位数字 :
:
mask = df['Value'].str.contains(r'\d{2}:\d{2}')
或 id 可能测试不等于 0
:
mask = df['Number'].ne(0)
然后创建新列并将 Value
替换为 NaN
s by mask
with Series.mask
and forward filling missing values, last filter by boolean indexing
:
df['Time'] = df['Value']
df['Value'] = df['Value'].mask(mask).ffill()
df = df[mask].copy()
print (df)
Value Number Time
1 Foo X 1 10:00
2 Foo X 2 10:00
3 Foo X 3 10:00
4 Foo X 4 10:00
6 Bar X 1 11:00
7 Bar X 2 11:00
9 Cat X 1 12:00
10 Cat X 2 12:00
11 Cat X 3 12:00
另一种使用遮罩和填充的解决方案:
(
df.assign(ind=df.Value.mask(df.Value.str.contains('^\d+:\d+')).ffill())
.loc[lambda x: x.Number.ne(0)]
.set_axis(['Time','Number', 'Value'], axis=1, inplace=False)
)