基于不同列的值复制行

28

我有一个交易数据框。每一行代表两个项目的交易(可以将其视为两张门票的交易)。我想根据售出数量复制每一行。

以下是示例代码:

# dictionary of transactions

d = {
    '1': ['20',  'NYC', '2'],
    '2': ['30',  'NYC', '2'],
    '3': ['5',   'NYC', '2'],
    '4': ['300', 'LA',  '2'],
    '5': ['30',  'LA',  '2'],
    '6': ['100', 'LA',  '2']
}

columns=['Price', 'City', 'Quantity']

# create dataframe and rename columns

df = pd.DataFrame.from_dict(
    data=d, orient='index'
)
df.columns = columns

这将产生一个类似于以下样式的数据框

Price   City    Quantity
20       NYC         2
30       NYC         2
5        NYC         2
300      LA          2
30       LA          2
100      LA          2

因此,在上述情况下,每一行将被转换为两个重复的行。如果“quantity”列的值为3,则该行将被转换为三个重复的行。


http://stackoverflow.com/help/mcve -- 你写的代码在哪里?你得到了什么输出结果?虽然你已经很清楚地描述了期望的输出,但这似乎不是一个适合在StackOverflow上提问的问题。请记住,SO是用于修复代码而不是设计和编写代码的。 - Prune
2
@Prune 我不同意,这似乎符合特定的编程问题描述。楼主可能需要一些指导来确定解决该问题的有效方法。有时候一个人甚至不知道从哪里开始,丑陋的解决方案只会混淆问题并浪费大家的时间。 - Andy Ross
1
我很感激@Prune的建议,而且我基本上同意他的观点。我认为这是过滤掉纯粹懒惰问题的好方法。然而,这个问题让我困惑了,我还没有能够提供一个有意义的解决方案的起点。 - MRA
3个回答

52
使用 repeat 进行答案
df.loc[df.index.repeat(df.Quantity)]
Out[448]: 
  Price City Quantity
1    20  NYC        2
1    20  NYC        2
2    30  NYC        2
2    30  NYC        2
3     5  NYC        2
3     5  NYC        2
4   300   LA        2
4   300   LA        2
5    30   LA        2
5    30   LA        2
6   100   LA        2
6   100   LA        2

12
没有冒犯其他贡献者的意思,但我认为这应该成为被采纳的答案 :) - Peter Leimbigler
但它可能需要更多的信息,至少需要一个链接到文档(https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Index.repeat.html)。 - sanzoghenzo
我喜欢这个答案。 <3 - Ken Jiiii

13

首先,我使用整数重新创建了您的数据,而不是文本。我还变化了数量,以便更容易理解问题。

d = {1: [20, 'NYC', 1], 2: [30, 'NYC', 2], 3: [5, 'SF', 3],      
     4: [300, 'LA', 1], 5: [30, 'LA', 2],  6: [100, 'SF', 3]}

columns=['Price', 'City', 'Quantity'] 
# create dataframe and rename columns

df = pd.DataFrame.from_dict(data=d, orient='index').sort_index()
df.columns = columns

>>> df
   Price City  Quantity
1     20  NYC         1
2     30  NYC         2
3      5   SF         3
4    300   LA         1
5     30   LA         2
6    100   SF         3

我使用了一个嵌套的列表推导式来创建一个新的DataFrame。

df_new = pd.DataFrame([df.ix[idx] 
                       for idx in df.index 
                       for _ in range(df.ix[idx]['Quantity'])]).reset_index(drop=True)
>>> df_new
    Price City  Quantity
0      20  NYC         1
1      30  NYC         2
2      30  NYC         2
3       5   SF         3
4       5   SF         3
5       5   SF         3
6     300   LA         1
7      30   LA         2
8      30   LA         2
9     100   SF         3
10    100   SF         3
11    100   SF         3

尝试这个:for _ in range(int(df.ix[idx]['Quantity'])) 你也可以显式地将列转换为整数:df['Quantity'] = dfQuantity.astype(int) - Alexander
好的,我已经解决了。在我的数据集中,索引是一个日期时间序列,其中有一些缺失的日期(不确定是否相关)。当我将索引设置为像上面示例中的常规连续整数时,它就可以工作了。供您参考,当我运行df.Quantity.unique()时,我得到"array([ 2, 4, 3, 1, 6, 5, 7, 8, 10, 9, 14, 12], dtype=int64)"。非常感谢您的帮助。 - MRA
是的,在这种情况下,你应该从 df.reset_index(inplace=True) 开始。 - Alexander
是的...对不起。我在做决定时遇到了很多麻烦 :( - MRA
没问题。Fabio比我更需要这个声望。 - Alexander
显示剩余2条评论

4
这个方法怎么样?我稍微改了一下你的数据,以便呼叫出售 4 张门票。
我们使用一个适当大小的 np.ones() 辅助数组,然后关键代码行是:a[np.arange(a.shape[1])[:] > a[:,0,np.newaxis]] = 0
我在这里看到了这种技术:numpy - update values using slicing given an array value
然后只需调用 .stack() 和一些基本过滤即可完成。
d = {'1': ['20', 'NYC', '2'], '2': ['30', 'NYC', '2'], '3': ['5', 'NYC', '2'], \
     '4': ['300', 'LA', '2'], '5': ['30', 'LA', '4'],  '6': ['100', 'LA', '2']}

columns=['Price', 'City', 'Quantity']
df = pd.DataFrame.from_dict(data=d, orient='index')
df.columns = columns
df['Quantity'] = df['Quantity'].astype(int)

# make a ones array 
my_ones = np.ones(shape=(len(df),df['Quantity'].max()))

# turn my_ones into a dataframe same index as df so we can join it to the right hand side. Plenty of other ways to achieve the same outcome. 
df_my_ones = pd.DataFrame(data =my_ones,index = df.index)

df = df.join(df_my_ones)

看起来像这样:

  Price City  Quantity  0  1  2  3
1    20  NYC         2  1  1  1  1
3     5  NYC         2  1  1  1  1
2    30  NYC         2  1  1  1  1
5    30   LA         4  1  1  1  1
4   300   LA         2  1  1  1  1

现在把“Quantity”列和“ones”合并到一个numpy数组中。
a = df.iloc[:,2:].values

这是聪明的部分。
a[np.arange(a.shape[1])[:] > a[:,0,np.newaxis]] = 0

并且重新分配回到df中。

df.iloc[:,2:] = a

现在df的样子如下,注意我们已经将Quantity后面的数字设为零:

  Price City  Quantity  0  1  2  3
1    20  NYC         2  1  1  0  0
3     5  NYC         2  1  1  0  0
2    30  NYC         2  1  1  0  0
5    30   LA         4  1  1  1  1
4   300   LA         2  1  1  0  0

df.set_index(['Price','City','Quantity'],inplace=True)
df =  df.stack().to_frame()
df.columns = ['sale_flag']
df.reset_index(inplace=True)
print df[['Price','City', 'Quantity']][df['sale_flag'] !=0]
print df

该功能会生成以下内容:

Price City  Quantity
0     20  NYC         2
1     20  NYC         2
4      5  NYC         2
5      5  NYC         2
8     30  NYC         2
9     30  NYC         2
12    30   LA         4
13    30   LA         4
14    30   LA         4
15    30   LA         4
16   300   LA         2
17   300   LA         2

非常有创意的解决方案!我得更仔细地研究一下。还可以查看下面的另一个响应,采用了不同的方法。非常感谢你的帮助,我非常感激。 - MRA

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接