Nous allons résoudre le problème Python de Data Science 100 Knock (traitement des données structurées). Ce groupe de questions utilise des pandas pour le traitement des données dans la réponse du modèle, mais nous le traiterons à l'aide de NumPy après étude.
: arrow_up: Premier article (# 1) : arrow_backward: article précédent (# 4) : arrow_forward: Article suivant (# 6)
En tant qu'étude de NumPy, je résoudrai le problème Python de Data Science 100 Knock (traitement des données structurées).
Beaucoup de gens qui font des activités de science des données avec Python sont peut-être des amateurs de pandas, mais en fait, vous pouvez faire de même avec NumPy sans utiliser ** pandas **. Et NumPy est généralement plus rapide. En tant que personne qui aime les pandas, je ne suis toujours pas habitué à utiliser NumPy, donc j'aimerais essayer de sortir des pandas en exploitant ce "Data Science 100 Knock" avec NumPy.
Cette fois, je répondrai aux 36e à 44e questions. Cela semble être le thème de la fusion de plusieurs trames de données. Les données initiales ont été lues comme suit (la spécification du type de données est pour le moment reportée).
import numpy as np
import pandas as pd
from numpy.lib import recfunctions as rfn
#Pour la réponse du modèle
df_category = pd.read_csv('data/category.csv', dtype='string')
df_customer = pd.read_csv('data/customer.csv')
df_product = pd.read_csv(
'data/product.csv',
dtype={col: 'string' for col in
['category_major_cd', 'category_medium_cd', 'category_small_cd']})
df_receipt = pd.read_csv('data/receipt.csv')
df_store = pd.read_csv('data/store.csv')
#Données que nous traitons
arr_category = np.genfromtxt(
'data/category.csv', delimiter=',', encoding='utf-8-sig',
names=True, dtype=tuple(['<U15']*6))
arr_customer = np.genfromtxt(
'data/customer.csv', delimiter=',', encoding='utf-8',
names=True, dtype=None)
arr_product = np.genfromtxt(
'data/product.csv', delimiter=',', encoding='utf-8-sig',
names=True, dtype=tuple(['<U10']*4+['<i4']*2))
arr_receipt = np.genfromtxt(
'data/receipt.csv', delimiter=',', encoding='utf-8',
names=True, dtype=None)
arr_store = np.genfromtxt(
'data/store.csv', delimiter=',', encoding='utf-8',
names=True, dtype=None)
Enfin, une fonction pour sortir le résultat du calcul sous forme de tableau structuré
def make_array(size, **kwargs):
arr = np.empty(size, dtype=[(colname, subarr.dtype)
for colname, subarr in kwargs.items()])
for colname, subarr in kwargs.items():
arr[colname] = subarr
return arr
P_036
P-036: Combinez en interne la trame de données de détail de reçu (df_receipt) et la trame de données de stockage (df_store), et affichez tous les éléments dans la trame de données de détail de reçu et 10 noms de magasin (nom de magasin) dans la trame de données de magasin.
Bien qu'il s'agisse d'une jointure interne, le contenu est RECHERCHEV. Tout d'abord, exécutez np.unique ()
sur la matrice qui combine les chaînes de clé des deux images pour la convertir en données numériques. Utilisez ensuite np.searchsorted ()
pour remplacer la colonne de code de magasin dans le relevé de réception.
In[023]
_, inv = np.unique(np.concatenate([arr_store['store_cd'],
arr_receipt['store_cd']]),
return_inverse=True)
inv_map, inv_arr = inv[:arr_store.size], inv[arr_store.size:]
sorter_index = np.argsort(inv_map)
idx = np.searchsorted(inv_map, inv_arr, sorter=sorter_index)
store_name = arr_store['store_name'][sorter_index[idx]]
new_arr = make_array(arr_receipt.size, **{col: arr_receipt[col]
for col in arr_receipt.dtype.names},
store_name=store_name)
new_arr[:10]
Out[023]
array([(20181103, 1257206400, 'S14006', 112, 1, 'CS006214000001', 'P070305012', 1, 158, 'Magasin Kuzugaya'),
(20181118, 1258502400, 'S13008', 1132, 2, 'CS008415000097', 'P070701017', 1, 81, 'Boutique Naruki'),
(20170712, 1215820800, 'S14028', 1102, 1, 'CS028414000014', 'P060101005', 1, 170, 'Magasin Futatsubashi'),
(20190205, 1265328000, 'S14042', 1132, 1, 'ZZ000000000000', 'P050301001', 1, 25, 'Magasin Shin Yamashita'),
(20180821, 1250812800, 'S14025', 1102, 2, 'CS025415000050', 'P060102007', 1, 90, 'Magasin Yamato'),
(20190605, 1275696000, 'S13003', 1112, 1, 'CS003515000195', 'P050102002', 1, 138, 'Magasin Komae'),
(20181205, 1259971200, 'S14024', 1102, 2, 'CS024514000042', 'P080101005', 1, 30, 'Magasin Mita'),
(20190922, 1285113600, 'S14040', 1102, 1, 'CS040415000178', 'P070501004', 1, 128, 'Magasin Nagatsuda'),
(20170504, 1209859200, 'S13020', 1112, 2, 'ZZ000000000000', 'P071302010', 1, 770, 'Magasin Jujo Nakahara'),
(20191010, 1286668800, 'S14027', 1102, 1, 'CS027514000015', 'P071101003', 1, 680, 'Magasin Minami Fujisawa')],
dtype=[('sales_ymd', '<i4'), ('sales_epoch', '<i4'), ('store_cd', '<U6'), ('receipt_no', '<i4'), ('receipt_sub_no', '<i4'), ('customer_id', '<U14'), ('product_cd', '<U10'), ('quantity', '<i4'), ('amount', '<i4'), ('store_name', '<U6')])
Dans le cas de la méthode utilisant pd.merge ()
de la réponse du modèle, elle est triée de force par la colonne clé (car la colonne clé n'est pas unique).
Si tel est le problème, si vous le faites avec des pandas
store_name = df_receipt['store_cd'].map(
df_store.set_index('store_cd')['store_name'])
df = df_receipt.assign(store_name=store_name)
Ca devrait être fait.
P_037
P-037: Combinez en interne le bloc de données produit (df_product) et le bloc de données de catégorie (df_category), et affichez tous les éléments du bloc de données produit et 10 noms de sous-catégorie (category_small_name) du bloc de données de catégorie.
le même.
In[037]
_, inv = np.unique(np.concatenate([arr_category['category_small_cd'],
arr_product['category_small_cd']]),
return_inverse=True)
inv_map, inv_arr = inv[:arr_category.size], inv[arr_category.size:]
sorter_index = np.argsort(inv_map)
idx = np.searchsorted(inv_map, inv_arr, sorter=sorter_index)
store_name = arr_category['category_small_name'][sorter_index[idx]]
new_arr = make_array(arr_product.size, **{col: arr_product[col]
for col in arr_product.dtype.names},
store_name=store_name)
new_arr[:10]
Out[037]
array([('P040101001', '04', '0401', '040101', 198, 149, 'Bento'),
('P040101002', '04', '0401', '040101', 218, 164, 'Bento'),
('P040101003', '04', '0401', '040101', 230, 173, 'Bento'),
('P040101004', '04', '0401', '040101', 248, 186, 'Bento'),
('P040101005', '04', '0401', '040101', 268, 201, 'Bento'),
('P040101006', '04', '0401', '040101', 298, 224, 'Bento'),
('P040101007', '04', '0401', '040101', 338, 254, 'Bento'),
('P040101008', '04', '0401', '040101', 420, 315, 'Bento'),
('P040101009', '04', '0401', '040101', 498, 374, 'Bento'),
('P040101010', '04', '0401', '040101', 580, 435, 'Bento')],
dtype=[('product_cd', '<U10'), ('category_major_cd', '<U10'), ('category_medium_cd', '<U10'), ('category_small_cd', '<U10'), ('unit_price', '<i4'), ('unit_cost', '<i4'), ('store_name', '<U15')])
P_038
P-038: Trouvez le montant total des ventes pour chaque client à partir du bloc de données client (df_customer) et du bloc de données de détail de réception (df_receipt). Cependant, pour les clients qui n'ont pas d'enregistrement d'achat, le montant des ventes doit être affiché à 0. En outre, les clients doivent cibler les personnes dont le code de genre (gender_cd) est féminin (1) et exclure les non-membres (identifiants clients commençant par "Z"). Seuls 10 résultats doivent être affichés.
De même, combinez d'abord les chaînes de clé des deux images, puis convertissez-les en données numériques. Ensuite, utilisez np.bincount ()
pour calculer le total pour chaque client, mais si vous utilisez le troisième argument minlength
à ce moment, la matrice de sortie sera de n'importe quelle taille, donc ʻunq.size` Est spécifié. Enfin, la valeur est obtenue en indexant le côté des données de l'article de reçu de la colonne de clé numérisée.
In[038]
is_member_receipt = arr_receipt['customer_id'].astype('<U1') != 'Z'
is_member_customer = ((arr_customer['customer_id'].astype('<U1') != 'Z')
& (arr_customer['gender_cd'] == 1))
customer = arr_customer['customer_id'][is_member_customer]
unq, inv = np.unique(
np.concatenate([customer, arr_receipt['customer_id'][is_member_receipt]]),
return_inverse=True)
customer_size = customer.size
amount_sum = np.bincount(
inv[customer_size:], arr_receipt['amount'][is_member_receipt], unq.size)
new_arr = make_array(customer_size,
customer_id=customer,
amount=amount_sum[inv[:customer_size]])
new_arr[:10]
Out[038]
array([('CS021313000114', 0.), ('CS031415000172', 5088.),
('CS028811000001', 0.), ('CS001215000145', 875.),
('CS015414000103', 3122.), ('CS033513000180', 868.),
('CS035614000014', 0.), ('CS011215000048', 3444.),
('CS009413000079', 0.), ('CS040412000191', 210.)],
dtype=[('customer_id', '<U14'), ('amount', '<f8')])
P_039
P-039: Extrayez les 20 premiers clients avec le plus grand nombre de jours de vente et les 20 premiers clients avec le montant total des ventes le plus élevé de la trame de données du relevé de réception (df_receipt), et effectuez une jointure externe complète. Cependant, les non-membres (identifiants clients commençant par «Z») doivent être exclus.
Utilisez np.partition ()
pour obtenir la 20e valeur de position, remplacez la valeur qui n'est pas dans les 20 premiers endroits par np.nan
, puis obtenez l'index.
In[039]
is_member = arr_receipt['customer_id'].astype('<U1') != 'Z'
unq, inv = np.unique(arr_receipt['customer_id'][is_member],
return_inverse=True)
sums = np.bincount(inv, arr_receipt['amount'][is_member], unq.size)
is_sum_top = sums >= -np.partition(-sums, 20)[20]
sums[~is_sum_top] = np.nan
unq2 = np.unique([inv, arr_receipt['sales_ymd'][is_member]], axis=-1)
counts = np.bincount(unq2[0]).astype(float)
is_cnt_top = counts >= -np.partition(-counts, 20)[20]
counts[~is_cnt_top] = np.nan
interserction = is_cnt_top | is_sum_top
make_array(interserction.sum(),
customer_id=unq[interserction],
amount=sums[interserction],
sales_ymd=counts[interserction])
Out[039]
array([('CS001605000009', 18925., nan), ('CS006515000023', 18372., nan),
('CS007514000094', 15735., nan), ('CS007515000107', nan, 18.),
('CS009414000059', 15492., nan), ('CS010214000002', nan, 21.),
('CS010214000010', 18585., 22.), ('CS011414000106', 18338., nan),
('CS011415000006', 16094., nan), ('CS014214000023', nan, 19.),
('CS014415000077', nan, 18.), ('CS015415000185', 20153., 22.),
('CS015515000034', 15300., nan), ('CS016415000101', 16348., nan),
('CS016415000141', 18372., 20.), ('CS017415000097', 23086., 20.),
('CS021514000045', nan, 19.), ('CS021515000056', nan, 18.),
('CS021515000089', 17580., nan), ('CS021515000172', nan, 19.),
('CS021515000211', nan, 18.), ('CS022515000028', nan, 18.),
('CS022515000226', nan, 19.), ('CS026414000059', 15153., nan),
('CS028415000007', 19127., 21.), ('CS030214000008', nan, 18.),
('CS030415000034', 15468., nan), ('CS031414000051', 19202., 19.),
('CS031414000073', nan, 18.), ('CS032414000072', 16563., nan),
('CS032415000209', nan, 18.), ('CS034415000047', 16083., nan),
('CS035414000024', 17615., nan), ('CS038415000104', 17847., nan),
('CS039414000052', nan, 19.), ('CS040214000008', nan, 23.)],
dtype=[('customer_id', '<U14'), ('amount', '<f8'), ('sales_ymd', '<f8')])
P_040
P-040: Je voudrais savoir combien de données seront obtenues en combinant tous les magasins et tous les produits. Calculez le nombre de produits directs des magasins (df_store) et des produits (df_product).
Je ne comprends pas l'intention du problème ...
In[040]
arr_store.size * arr_product.size
Out[040]
531590
Time[040]
#Le modèle de réponse
%%timeit
df_store_tmp = df_store.copy()
df_product_tmp = df_product.copy()
df_store_tmp['key'] = 0
df_product_tmp['key'] = 0
len(pd.merge(df_store_tmp, df_product_tmp, how='outer', on='key'))
# 277 ms ± 6.09 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
# NumPy
%timeit arr_store.size * arr_product.size
# 136 ns ± 1.69 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
P_041
P-041: Agréger le montant des ventes (montant) du bloc de données des détails de la réception (df_receipt) pour chaque date (sales_ymd) et calculer l'augmentation / la diminution du montant des ventes du jour précédent. Il suffit d'afficher 10 résultats de calcul.
Utilisez np.ediff1d ()
pour obtenir la différence avant et après dans le tableau.
In[041]
unq, inv = np.unique(arr_receipt['sales_ymd'], return_inverse=True)
diff_amount = np.ediff1d(np.bincount(inv, arr_receipt['amount']))
make_array(unq.size, sales_ymd=unq,
diff_amount=np.concatenate([[np.nan], diff_amount]))[:10]
Out[041]
array([(20170101, nan), (20170102, -9558.), (20170103, 3338.),
(20170104, 8662.), (20170105, 1665.), (20170106, -5443.),
(20170107, -8972.), (20170108, 1322.), (20170109, 1981.),
(20170110, -6575.)],
dtype=[('sales_ymd', '<i4'), ('diff_amount', '<f8')])
P_042
P-042: Agréger le montant des ventes (montant) du bloc de données de détail de la réception (df_receipt) pour chaque date (sales_ymd), et combiner les données d'il y a 1 jour, 2 jours et 3 jours avec les données de chaque date. Seuls 10 résultats doivent être affichés.
Prenez-le en tranches.
In[042]
unq, inv = np.unique(arr_receipt['sales_ymd'], return_inverse=True)
amount = np.bincount(inv, arr_receipt['amount'])
make_array(unq.size - 3,
sales_ymd=unq[3:], amount=amount[3:],
lag_ymd_1=unq[2:-1], lag_amount_1=amount[2:-1],
lag_ymd_2=unq[1:-2], lag_amount_2=amount[1:-2],
lag_ymd_3=unq[:-3], lag_amount_3=amount[:-3])[:10]
Out[042]
array([(20170104, 36165., 20170103, 27503., 20170102, 24165., 20170101, 33723.),
(20170105, 37830., 20170104, 36165., 20170103, 27503., 20170102, 24165.),
(20170106, 32387., 20170105, 37830., 20170104, 36165., 20170103, 27503.),
(20170107, 23415., 20170106, 32387., 20170105, 37830., 20170104, 36165.),
(20170108, 24737., 20170107, 23415., 20170106, 32387., 20170105, 37830.),
(20170109, 26718., 20170108, 24737., 20170107, 23415., 20170106, 32387.),
(20170110, 20143., 20170109, 26718., 20170108, 24737., 20170107, 23415.),
(20170111, 24287., 20170110, 20143., 20170109, 26718., 20170108, 24737.),
(20170112, 23526., 20170111, 24287., 20170110, 20143., 20170109, 26718.),
(20170113, 28004., 20170112, 23526., 20170111, 24287., 20170110, 20143.)],
dtype=[('sales_ymd', '<i4'), ('amount', '<f8'), ('lag_ymd_1', '<i4'), ('lag_amount_1', '<f8'), ('lag_ymd_2', '<i4'), ('lag_amount_2', '<f8'), ('lag_ymd_3', '<i4'), ('lag_amount_3', '<f8')])
P_043
P-043: Cadre de données récapitulatif des ventes (df_sales_summary) qui combine le bloc de données de détail des reçus (df_receipt) et le bloc de données client (df_customer) et totalise le montant des ventes (montant) pour chaque sexe (sexe) et âge (calculé à partir de l'âge). ). Le sexe est 0 pour l'homme, 1 pour la femme et 9 pour l'inconnu.
Cependant, la composition de l'article doit être de quatre éléments: l'âge, le montant des ventes pour les femmes, le montant des ventes pour les hommes et le montant des ventes pour le sexe inconnu (tabulation croisée de l'âge verticalement et du sexe horizontalement). En outre, le groupe d'âge devrait être tous les 10 ans.
Tout d'abord, les chaînes de clé des deux images sont combinées puis converties en données numériques. Ensuite, créez une matrice map_array
remplie de valeurs manquantes, et insérez l'âge et le sexe dans la matrice en utilisant l'ID client numérisé du côté des données client comme index. Après cela, l'ID client quantifié du côté des données de l'article de reçu est utilisé comme index pour acquérir l'âge et le sexe. Enfin, créez un plan bidimensionnel d'âge et de sexe, indiquez le sexe et l'âge et ajoutez le montant des ventes.
In[043]
#Convertir l'identifiant client en données numériques
unq, inv = np.unique(np.concatenate([arr_customer['customer_id'],
arr_receipt['customer_id']]),
return_inverse=True)
inv_map, inv_arr = inv[:arr_customer.size], inv[arr_customer.size:]
#Acquisition de l'âge (valeur manquante=0)
map_array = np.zeros(unq.size, dtype=int)
map_array[inv_map] = arr_customer['age']//10
arr_age = map_array[inv_arr]
max_age = arr_age.max()+1
#Acquisition du genre (valeur manquante=9)
# map_array = np.full(unq.size, 9, dtype=int)
map_array[:] = 9
map_array[inv_map] = arr_customer['gender_cd']
arr_gender = map_array[inv_arr]
#Ventes totales sur un plan bidimensionnel d'âge et de sexe
arr_sales_summary = np.zeros((max_age, arr_gender.max()+1), dtype=int)
np.add.at(arr_sales_summary, (arr_age, arr_gender), arr_receipt['amount'])
#Convertir en un tableau structuré
make_array(max_age,
era=np.arange(max_age)*10,
male=arr_sales_summary[:, 0],
female=arr_sales_summary[:, 1],
unknown=arr_sales_summary[:, 9])
Out[043]
array([( 0, 0, 0, 12395003), (10, 1591, 149836, 4317),
(20, 72940, 1363724, 44328), (30, 177322, 693047, 50441),
(40, 19355, 9320791, 483512), (50, 54320, 6685192, 342923),
(60, 272469, 987741, 71418), (70, 13435, 29764, 2427),
(80, 46360, 262923, 5111), (90, 0, 6260, 0)],
dtype=[('era', '<i4'), ('male', '<i4'), ('female', '<i4'), ('unknown', '<i4')])
Time[043]
Réponse du modèle: 177 ms ± 3.45 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
NumPy:66.4 ms ± 1.28 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
P_044
P-044: La base de données de synthèse des ventes (df_sales_summary) créée à la question précédente était une vente côte à côte de sexe. Tenons le sexe verticalement à partir de cette base de données et convertissons-le en 3 éléments: âge, code de sexe et montant des ventes. Cependant, le code de genre est «00» pour les hommes, «01» pour les femmes et «99» pour les inconnues.
In[044]
arr_amount = arr_sales_summary[:, [0, 1, 9]].ravel()
make_array(arr_amount.size,
era=(np.arange(max_age)*10).repeat(3),
gender_cd=np.tile(np.array(['00', '01', '99']), max_age),
amount=arr_amount)
Out[044]
array([( 0, '00', 0), ( 0, '01', 0), ( 0, '99', 12395003),
(10, '00', 1591), (10, '01', 149836), (10, '99', 4317),
(20, '00', 72940), (20, '01', 1363724), (20, '99', 44328),
(30, '00', 177322), (30, '01', 693047), (30, '99', 50441),
(40, '00', 19355), (40, '01', 9320791), (40, '99', 483512),
(50, '00', 54320), (50, '01', 6685192), (50, '99', 342923),
(60, '00', 272469), (60, '01', 987741), (60, '99', 71418),
(70, '00', 13435), (70, '01', 29764), (70, '99', 2427),
(80, '00', 46360), (80, '01', 262923), (80, '99', 5111),
(90, '00', 0), (90, '01', 6260), (90, '99', 0)],
dtype=[('era', '<i4'), ('gender_cd', '<U2'), ('amount', '<i4')])
Recommended Posts